#!/bin/bash
# ======================================================================
# $Source: $
# $Revision: $
# $Name: $
# $Date: $
# $State: $
# $Author: $
# ======================================================================

# ======================================================================
# This script checks that the various files, directories, and mount
# points needed by the cron daemon exist and have the proper settings,
# permissions, etc.  This script is based primarily on the requirements
# listed in the text file /usr/share/doc/Cygwin/cron.README.
# ======================================================================

# ======================================================================
# Routine: check_program
# Check to see that a specified program ($1) is installed and accessible
# by this script.  If it is not, then alert the user about which package
# ($2) should be installed to provide that program.
# ======================================================================
function check_program() {

	type $1 > /dev/null 2>&1;
    if [ $? -ne 0 ]; then
		echo "The '$1' program is missing or not in your PATH."
		echo "This program is included in the '$2' package."
		echo "Please install this program and run this script again."
		return 1
	fi

} # === End of check_program() === #


# ======================================================================
# Routine: sanity_check
# Check for the set of programs that are used by this script.
# ======================================================================
function sanity_check() {

	# Check for programs that this script uses.
	check_program awk gawk || return $?
	check_program ls fileutils || return $?
	check_program egrep grep || return $?
	check_program fgrep grep || return $?
	check_program id sh-utils || return $?
	check_program cut textutils || return $?
	check_program mount cygwin || return $?
	check_program cygcheck cygwin || return $?

} # === End of sanity_check() === #


# ======================================================================
# Routine: check_passwd_and_group
# Check to see whether the user's password ID and group exist in the
# system /etc/passwd and /etc/group files, respectively.
# ======================================================================
function check_passwd_and_group() {

	if [ "$(id -gn)" = mkpasswd ]; then
		echo "It appears that you do not have an entry for your user ID"
		echo "in /etc/passwd.  If this check is incorrect, then re-run"
		echo "this script with the '-f' command-line option."
		echo
		echo "Otherwise, use the 'mkpasswd' utility to generate an"
		echo "entry for your User ID in the password file:"
		echo "   mkpasswd -l -u [User ID] >> /etc/passwd"
		echo "or"
		echo "   mkpasswd -d -u [User ID] >> /etc/passwd"
		echo "and then run this script again."
		return 1

	elif [ -n "$USERDOMAIN" ] &&  [ -n "$USERNAME" ]; then
		egrep -q -i "^$(id -un):.*U-$USERDOMAIN\\\\$USERNAME" /etc/passwd;
		if [ $? -ne 0 ]; then
			echo "It appears that you do not have an entry for:"
			echo "   $USERDOMAIN\\$USERNAME"
			echo "in /etc/passwd."
			echo
			echo "Use the 'mkpasswd' utility to generate an entry for"
			echo "your User ID in the password file:"
			echo "   mkpasswd -d -u [User ID] >> /etc/passwd"
			echo "and then run this script again."
			return 1
		fi
	fi

	if [ "$(id -gn)" = mkgroup ]; then
		echo "It appears that you do not have an entry for your group ID"
		echo "in /etc/group.  If this check is incorrect, then re-run"
		echo "this script with the '-f' command-line option."
		echo
		echo "Otherwise, use the 'mkgroup' utility to generate an"
		echo "entry for your group ID in the password file:"
		echo "   mkgroup -l -u > /etc/group"
		echo "or"
		echo "   mkgroup -d -u > /etc/group"
		echo "and then run this script again."
		return 1
	fi

	local fname=""
	for fname in /etc/passwd /etc/group; do
		/usr/bin/ls -ld $fname | egrep -q '^-r..r..r.. ';
		if [ $? -ne 0 ]; then
			echo "The permissions on the file $fname are not correct."
			echo "Please run 'chmod +r $fname', and run this script again."
			return 1
		fi
	done

} # === End of check_passwd_and_group() === #


# ======================================================================
# Routine: check_dir
# Check to see that the specified directory ($1) exists.
# ======================================================================
function check_dir() {

	if [ ! -d $1 ]; then
		echo "Your computer does not appear to have a $1 directory."
		echo "Please investigate this problem, and run this script again."
		return 1
	fi

	/usr/bin/ls -ld $1 | egrep -q '^dr[-w]x.*';
	if [ $? -ne 0 ]; then
		echo "The permissions on the directory $1 are not correct."
		echo "Please run 'chmod u+rx $1', and run this script again."
		return 1
	fi

} # === End of check_dir() === #


# ======================================================================
# Routine: check_dir_perms
# Check to see that the specified directory ($1) exists and has the
# required permissions, as described in /usr/share/doc/Cygwin/cron.README.
# ======================================================================
function check_dir_perms() {

	check_dir $1 || return $?

	/usr/bin/ls -ld $1 | fgrep -q 'drwxrwxrwt';
	if [ $? -ne 0 ]; then
		echo "The permissions on the directory $1 are not correct."
		echo "Please run 'chmod 1777 $1', and run this script again."
		return 1
	fi

} # === End of check_dir_perms() === #


# ======================================================================
# Routine: check_var_run
# Check to see that SYSTEM or the Administrators group has write
# permission in the directory /var/run.  This permission is needed
# so that the cron.pid file can be created by the cron service.
# ======================================================================
function check_var_run() {

	# We check from least- to most-restrictive permission.

	# If 'everyone' / 'other' does not have write permission, then
	# check to see whether group permissions are sufficient.
	if [ $(/usr/bin/ls -dl /var/run | cut -b9) != w ]; then
		# If 'Administrators' has group access to /var/run, but does not have
		# write permission, then check to see whether user permissions are
		# sufficient.
		if [ $(/usr/bin/ls -dl /var/run | awk '{ print $4 }') = Administ ] && \
			[ $(/usr/bin/ls -dl /var/run | cut -b6) != w ]; then
			# If SYSTEM is the owner of /var/run and does not have write
			# permission, then notify the user.
			if [ $(/usr/bin/ls -dln /var/run | awk '{ print $4 }') -eq 18 ]; then
				if [ $(/usr/bin/ls -dl /var/run | cut -b3) != w ]; then
					echo "The directory /var/run cannot be written to by its owner."
					echo "Please check/change the permissions of /var/run and run"
					echo "this script again.  For example:"
					echo "   chmod u+w /var/run"
					return 1
				fi
			else
				echo "The SYSTEM user or Administrators group needs to have"
				echo "write permission in the directory /var/run.  Please"
				echo "check/change the user and/or group ownership and"
				echo "permissions and run this script again."
				return 1
			fi
		fi
	fi

} # === End of check_var_run() === #


# ======================================================================
# Routine: check_sys_mount
# Check to see that the SYSTEM account has access to the specified
# directory.
# ======================================================================
function check_sys_mount() {

	local mnt_point=$1
	local dos_dir=$2

	if ! mount | egrep -qe ".+ on $mnt_point .+system.+"; then
		echo "The SYSTEM user cannot access the mount point ${mnt_point}."
		echo "Please run the following command to add a system mount point:"
		echo '   mount -f -s -b "[DOS path to Cygwin]'$dos_dir\" \"$mnt_point\"
		echo "where [DOS path to Cygwin] is something like c:/cygwin."
		echo
		echo "For more information, run 'mount -m' and 'mount -h'"
		echo
		echo "After adding this mount point, please re-run this script."
		return 1
	fi

} # === End of check_sys_mount() === #


# ======================================================================
# Routine: check_cron_table
# Check for the existence of a crontab for the user, and check its
# permissions and ownership.
# ======================================================================
function check_cron_table() {

	local user_id=$(id -un)
	local cron_table=/var/cron/tabs/$user_id

	if [ ! -f $cron_table ]; then
		echo "Your computer does not appear to have a crontab for $user_id."
		echo "Please generate a crontab for $user_id using 'crontab -e',"
		echo "and run this script again."
		return 1
	fi

	/usr/bin/ls -l $cron_table | fgrep -q 'rw-r-----';
	if [ $? -ne 0 ]; then
		echo "The permissions of your crontab file are set to:"
		/usr/bin/ls -l $cron_table
		echo "They need to be set to read/write for $user_id and"
		echo "to read-only for group.  You can set these with"
		echo "	chmod 640 $cron_table"
		echo "Please check your crontab's permissions, and run"
		echo "this script again."
		return 1
	fi

	/usr/bin/ls -l $cron_table | awk '{ print $4 }' | fgrep -q SYSTEM;
	if [ $? -ne 0 ]; then
		echo "The group membership of your crontab file should be SYSTEM,"
		echo "as documented in the file /usr/share/doc/Cygwin/cron.README."
		echo "You can change this setting with:"
		echo "	 chgrp SYSTEM $cron_table"
		echo "Please check your crontab's group membership, and"
		echo "run this script again."
		return 1
	fi

	/usr/bin/ls -ln $cron_table | awk '{ print $4 }' | fgrep -q 18;
	if [ $? -ne 0 ]; then
		echo "The value of SYSTEM in your /etc/group file needs to"
		echo "be the reserved number '18', as documented in"
		echo "/usr/share/doc/Cygwin/cron.README.  Please investigate this"
		echo "and run this script again."
		return 1
	fi

} # === End of check_cron_table() === #


function main() {

	echo "cron_diagnose.sh 1.5"
	echo

	sanity_check || return $?

	if [ "$1" != '-f' ]; then  # Allow the user to skip the password check.
		check_passwd_and_group || return $?
	fi

	# Check the integrity of the files in the 'cron' package:
    cygcheck -c cron | fgrep -q 'Incomplete'
	if [ $? -eq 0 ]; then
		echo "'cygcheck -c cron' reports that your cron installation"
		echo "is incomplete.  Please consider running 'setup.exe' and"
		echo "selecting 'Reinstall' from the install options, and then"
		echo "run this script again."
		return 1
	fi

	check_dir /etc/cron.d || return $?
	check_dir /var || return $?

	check_dir_perms /var/cron || return $?
	check_dir_perms /var/cron/tabs || return $?

	check_var_run || return $?

	check_sys_mount /usr/bin /bin || return $?
	check_sys_mount /usr/lib /lib || return $?
	check_sys_mount / / || return $?

	check_cron_table || return $?

	echo "This script did not find any errors in your crontab setup."
	echo "If you are still unable to get cron to work, then try"
	echo "shutting down the cron service, uninstalling it,"
	echo "reinstalling it, and restarting it."
	echo
	echo "The following commands will do that:"
	echo "  $ cygrunsrv --stop cron"
	echo "  $ cygrunsrv --remove cron"
	echo "  $ cygrunsrv --install cron -p /usr/sbin/cron -a -D"
	echo "  $ cygrunsrv --start cron"
	echo 

	if [ -f /var/run/cron.pid ]; then
		echo "If the cron service does not start, try deleting the file"
		echo "/var/run/cron.pid and then repeating the commands above."
		echo
	fi

	echo "If none of this fixes the problem, then report your problem"
	echo "to cygwin@cygwin.com.  Please include a copy of your crontab,"
	echo "('crontab -l') and the output of 'cygcheck -srv > cygcheck.txt'."
	echo
	echo "Please include the generated files 'cygcheck.txt' *as an attachment*,"
	echo "and NOT in the body of the mail message."

} # === End of main() === #

# Entry point:
main $@
exit $?

# === End of $RCSfile$ === #

