cron-config: Please test
Pierre A. Humblet
Pierre.Humblet@ieee.org
Wed Dec 29 01:13:00 GMT 2004
The goal of the attached cron-config script is to setup
a cron service on any Windows platform, from Win95 to Win2003,
and to verify that the environment is sane.
The hope is to reduce the rate of complaints to the list.
The plan is to make it part of the Cygwin cron package.
It includes most of the functionality of cron_diagnose.sh,
provided by Mark Harig.
I would appreciate getting feedback about any glitches,
particularly from Windows 2003 users. To test the
script, please use the most recent version of the cron
package. Feel free to send your comments directly to me.
Pierre
-------------- next part --------------
#!/bin/sh
#set -x
# ======================================================================
# This script initializes and possibly starts a cron daemon.
# It includes numerous tests to insure that the environmnet is sound.
# On Windows 2003 it is capable of creating a privileged user.
# ======================================================================
# ======================================================================
# Routine: getvalue
# Get a verified non-empty string in variable "value"
# ======================================================================
getvalue() {
while true
do
echo -n "$1 "
if read value; then
if [ -n "${value}" ]; then
echo -n "Reenter: "
read verify
[ "${verify}" = "${value}" ] && return 0;
fi
else
echo -e "Quitting.\n"
exit 1
fi
done
} # === End of getvalue() === #
# ======================================================================
# Routine: request
# Get a yes/no anwer
# ======================================================================
request() {
while true
do
echo -n "$1 (yes/no) "
if read answer
then
if [ "${answer}" = "yes" ]
then
return 0
elif [ "${answer}" = "no" ]
then
return 1
fi
else
echo -e "Quitting.\n"
exit 1
fi
done
} # === End of request() === #
# ======================================================================
# 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.
# ======================================================================
check_program() {
unset -f "$1"
prog="$1"
if [ ! -e "/usr/bin/${prog}" ]; then
echo "The '$1' program is not in /usr/bin."
echo "This program is included in the \'$2\' package."
echo "Please install this program."
echo
return 1
elif [ ! -x "/usr/bin/${prog}" ]; then
echo "The '$1' program (/usr/bin/${prog}') is not executable."
echo
return 1
fi
} # === End of check_program() === #
# ======================================================================
# Routine: sanity_check
# Check for the set of programs that are used by this script.
# ======================================================================
sanity_check() {
ret=0
# Check for programs that this script uses.
check_program awk gawk || ret=1
check_program ls coreutils || ret=1
check_program grep grep || ret=1
check_program sed sed || ret=1
check_program id coreutils || ret=1
check_program cut coreutils || ret=1
check_program uname coreutils || ret=1
check_program cygcheck cygwin || ret=1
check_program regtool cygwin || ret=1
return "${ret}"
} # === End of sanity_check() === #
# ======================================================================
# Routine: get_NT
# ======================================================================
get_NT() {
nt2003=""
nt=$(uname -s | sed -ne 's/^CYGWIN_NT-\([^ ]*\)/\1/p')
[ "$nt" \> 5.1 ] && nt2003=yes
[ -z "$nt" ] && return 1
return 0
} # === End of get_NT() === #
# ======================================================================
# Routine: warning_for_etc_file
# Display a warning message for the user about overwriting the specified
# file in /etc.
# ======================================================================
warning_for_etc_file() {
echo
echo "WARNING: The command above overwrites any existing /etc/$1."
echo "You may want to preserve /etc/$1 before generating a new,"
echo "one, and then compare your saved /etc/$1 file with the"
echo "newly-generated one in case you need to restore other"
echo "entries."
echo
} # === warning_for_etc_file() === #
# ======================================================================
# Routine: get_system_and_admins_gids
# Get the ADMINs ids from /etc/group and /etc/passwd
# ======================================================================
get_system_and_admins_ids() {
ret=0
for fname in /etc/passwd /etc/group; do
if ls -ld "${fname}" | grep -Eq '^-r..r..r..'; then
true
else
echo "The file $fname is not readable by all."
echo "Please run 'chmod +r $fname'."
echo
ret=1
fi
done
[ ! -r /etc/passwd -o ! -r /etc/group ] && return 1;
ADMINSGID=$(sed -ne '/^[^:]*:S-1-5-32-544:.*:/{s/[^:]*:[^:]*:\([0-9]*\):.*$/\1/p;q}' /etc/group)
SYSTEMGID=$(sed -ne '/^[^:]*:S-1-5-18:.*:/{s/[^:]*:[^:]*:\([0-9]*\):.*$/\1/p;q}' /etc/group)
if [ -z "$ADMINSGID" -o -z "$SYSTEMGID" ]; then
echo "It appears that you do not have entries for the"
echo "ADMINISTRATORS and/or SYSTEM sids in /etc/group."
echo
echo "Use the 'mkgroup' utility to generate them"
echo " mkgroup -l > /etc/group"
warning_for_etc_file group
ret=1;
fi
ADMINSUID=$(sed -ne '/^[^:]*:[^:]*:[0-9]*:[0-9]*:[^:]*,S-1-5-32-544:.*:/{s/[^:]*:[^:]*:\([0-9]*\):.*$/\1/p;q}' /etc/passwd)
SYSTEMUID=$(sed -ne '/^[^:]*:[^:]*:[0-9]*:[0-9]*:[^:]*,S-1-5-18:.*:/{s/[^:]*:[^:]*:\([0-9]*\):.*$/\1/p;q}' /etc/passwd)
if [ -z "$ADMINSUID" -o -z "$SYSTEMUID" ]; then
echo "It appears that you do not have an entry for the"
echo "ADMINISTRATORS and/or SYSTEM sids in /etc/passwd."
echo
echo "Use the 'mkpasswd' utility to generate it"
echo " mkpasswd -l > /etc/passwd."
warning_for_etc_file passwd
ret=1;
fi
return "${ret}"
} # === get_system_and_admins_ids() === #
# ======================================================================
# 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.
# ======================================================================
check_passwd_and_group() {
ret=0
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
ret=1
elif [ -n "$USERDOMAIN" ] && [ -n "$USERNAME" ]; then
if ! grep -E -q -i "^$(id -un):.*U-$USERDOMAIN\\\\$USERNAME" /etc/passwd; 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
ret=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 -g Group_id >> /etc/group"
echo "or"
echo " mkgroup -d -g Group_id >> /etc/group."
echo
ret=1
fi
return "${ret}"
} # === End of check_passwd_and_group() === #
# ======================================================================
# Routine: check_dir
# Check to see that the specified directory ($1) exists.
# ======================================================================
check_dir() {
if [ ! -d $1 ]; then
echo "Your computer does not appear to have a $1 directory."
echo "Please investigate this problem, and then run this script again."
echo
return 1
fi
if ls -ld $1 | grep -E -q '^dr[-w]x.*'; then
true
else
echo "The permissions on the directory $1 are not correct."
echo "Please run 'chmod u+rx $1'."
echo
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.
# ======================================================================
check_dir_perms() {
check_dir $1 || return 1
if ls -ld $1 | grep -F -q 'drwxrwxrwt'; then
true
else
echo "The permissions on the directory $1 are not correct."
echo "Please run 'chmod 1777 $1'."
echo
return 1
fi
} # === End of check_dir_perms() === #
# ======================================================================
# Routine: check_access
# Check to see that the owner and Administrators have
# proper access to the file or directory.
# On installations older than Windows 2003, allow access by System
# ======================================================================
check_access() {
file="$1"
perm="$2"
notify=0;
ls="$(ls -dLln "$file" 2> /dev/null)"
# If the owner of the file does not have access,
# then notify the user.
if [ -z "$(echo "$ls" | sed -n /^."$perm"/p)" ]; then
notify=1;
# If 'Administrators' has owner or group access to the file,
# but does not have the desired access, then notify the user.
elif [ "$(echo "$ls" | awk '{ print $3 }')" -eq $ADMINSUID \
-o \( -z "$nt2003" -a "$(echo "$ls" | awk '{ print $3 }')" -eq $SYSTEMUID \) ]; then
true;
elif [ "$(echo "$ls" | awk '{ print $4 }')" -eq $ADMINSGID \
-o \( -z "$nt2003" -a "$(echo "$ls" | awk '{ print $4 }')" -eq $SYSTEMGID \) ]; then
if [ -z "$(echo "$ls" | sed -n /^...."$perm"/p)" ]; then notify=1; fi
elif [ -z "$(echo "$ls" | sed -n /^......."$perm"/p)" ]; then notify=1; fi
if [ "$notify" -eq 1 ]; then
echo "The owner and the Administrators need";
echo "to have $perm permission to $file.";
echo "Here are the current permissions:";
ls -dlL "${file}";
echo;
echo "Please change the user and/or group ownership and";
echo "permissions of $file.";
echo
return 1;
fi
} # === End of check_access() === #
# ======================================================================
# Routine: check_sys_mount
# Check to see that the SYSTEM account has access to the specified
# directory.
# ======================================================================
check_sys_mount() {
mnt_point=$1
dos_dir=$2
SYSTEM_MOUNTS='/proc/registry/HKey_Local_Machine/Software/Cygnus Solutions/Cygwin/mounts v2'
if ls "$SYSTEM_MOUNTS" | grep -Eq "^${mnt_point}\$"; then
true
else
echo;
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
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.
# ======================================================================
check_cron_table() {
cron_table="/var/cron/tabs/${USER}"
if [ ! -f $cron_table ]; then
echo "WARNING: Your computer does not appear to have a cron table for ${USER}."
echo "Please generate a cron table for ${USER} using 'crontab -e'"
echo
return 0
fi
fail=0
ls="$(ls -ln "$cron_table")"
if echo "$ls" | grep -Eq '^-rw-r-----'; then
true
else
echo "The file permissions of your cron table need to"
echo "provide read/write access for ${USER}."
echo "The permissions of your cron table file are set to:"
ls -l $cron_table
echo
echo "You can set the file permissions with:"
echo " chmod 640 $cron_table"
echo "Please change your cron table's permissions."
echo
fail=1
fi
if [ "$(echo "$ls" | awk '{ print $4 }')" -ne "$ADMINSGID" \
-a \( -n "$nt2003" -o "$(echo "$ls" | awk '{ print $4 }')" -ne "$SYSTEMGID" \) ]; then
echo "The group membership of your cron table file should be ADMINISTRATORS,"
echo "as documented in the file /usr/share/doc/Cygwin/cron.README."
echo "Here is your cron table file:"
ls -l $cron_table
echo
echo "You can change the group membership setting with:"
echo " chgrp $ADMINSGID $cron_table"
echo "Please change your cron table's group membership."
echo
fail=1
fi
return "${fail}"
} # === End of check_cron_table() === #
# ======================================================================
# Routine: check_myself
# Check for the existence and accessibility of key files
# ======================================================================
check_myself() {
ret=0
if [ ! -x /usr/sbin/cron ]; then
echo "ERROR: You need x access to /usr/sbin/cron.";
echo
ret=1
fi
if [ ! -w /var/run ]; then
echo "ERROR: You need w access to /var/run.";
echo
ret=1
fi
if [ ! -w /var/log ]; then
echo "ERROR: You need w access to /var/log.";
echo
ret=1
fi
if [ ! -x /usr/sbin/sendmail ]; then
echo "WARNING: /usr/sbin/sendmail should point to an executable mailer";
echo " such as ssmtp or exim."
echo
fi
if [ ! -e /var/cron/tabs/${USER} ]; then
echo "WARNING: You do not currently have a crontab file."
echo
elif [ ! -r /var/cron/tabs/${USER} ]; then
echo "ERROR: Your crontab file is not readable."
echo
ret=1
fi
return ${ret}
} # === End of check_myself() === #
# ======================================================================
# Routine: cron_diagnose
# Checks the environment.
# "nt" and "username" must be set.
# ======================================================================
cron_diagnose() {
# Check the integrity of the files in the 'cron' package:
if cygcheck -c cron | grep -F -q 'Incomplete'; 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."
echo
return 1
fi
if [ -n "$nt" ]
then
get_system_and_admins_ids || return 1
check_passwd_and_group || return 1
fi
if [ -z "$nt" -o "$username" = "$USER" ]
then
check_myself
return $?
fi
ret=0
check_sys_mount /usr/bin /bin || ret=1
check_sys_mount /usr/lib /lib || ret=1
check_sys_mount / / || ret=1
check_dir /etc/cron.d || ret=1
check_dir /var || ret=1
check_dir_perms /var/cron || ret=1
check_dir_perms /var/cron/tabs || ret=1
# Check write access to /var/run, to create cron_pid
check_access /var/run .w. || ret=1
# Check write access to /var/log, to create cron.log
check_access /var/log .w. || ret=1
# Check x access to /usr/sbin/cron
check_access /usr/sbin/cron ..x || ret=1
# Check x access to /usr/sbin/sendmail
check_access /usr/sbin/sendmail ..x ||
echo " ssmtp and exim are suitable mailers."
check_cron_table || ret=1
return "${ret}"
} # === End of cron_diagnose() === #
# ======================================================================
# Routine: create_user
# Create a privileged user on Windows 2003
# ======================================================================
create_user() {
SYSCONFDIR="/etc"
first_account=""
accounts=""
for username in cyg_server cron_server sshd_server
do
if net user "${username}" 1> /dev/null 2>&1
then
[ -z "${first_account}" ] && first_account="${username}"
accounts="${accounts}'${username}' "
fi
done
if [ -n "${accounts}" ]; then
echo "The following accounts were found: ${accounts}."
username="${first_account}"
else
echo "No privileged account could be found."
username="cyg_server"
fi
echo "This script plans to use ${username}."
if request "Do you want to use another name?"; then
getvalue "Enter the new user name: "
username="${value}"
fi
net user "${username}" >/dev/null 2>&1 && username_in_sam=yes
if [ "$username_in_sam" != "yes" ]
then
echo
echo "User $username needs a password. It must match"
echo "the rules in force on your system."
getvalue "Please enter the password: "
_password="${value}"
net user "${username}" "${_password}" /add /fullname:"Privileged server" /yes > /tmp/nu.$$ 2>&1 && username_in_sam=yes
if [ "${username_in_sam}" != "yes" ]
then
echo "Creating the user '"${username}"' failed! Reason:"
cat /tmp/nu.$$
rm /tmp/nu.$$
exit 1
else
echo
echo "User '"${username}"' has been created with password '${_password}'."
echo "If you change the password, please keep in mind to change the password"
echo "for the cron service, too."
echo
fi
passwd_has_expiry_flags=`passwd -v | awk '/^passwd /{print ( $3 >= 1.5 ) ? "yes" : "no";}'`
if [ "${passwd_has_expiry_flags}" != "yes" ]
then
echo "WARNING: User "${username}" has password expiry set to system default."
echo "Please check that password never expires or set it to your needs."
echo
elif ! passwd -e "${username}"
then
echo "WARNING: Setting password expiry for user "${username}" failed!"
echo "Please check that password never expires or set it to your needs."
echo
fi
fi
_admingroup="$( mkgroup -l | awk -F: '{if ( $2 == "S-1-5-32-544" ) print $1;}' )"
if [ -z "${_admingroup}" ]
then
echo "ERROR: Cannot obtain the Administrators group name."
exit 1
fi
if net localgroup "${_admingroup}" | grep -Eiq "^${username}.?\$"; then
true
else
net localgroup "${_admingroup}" "${username}" /add > /dev/null 2>&1 && username_in_admingroup=yes
if [ "${username_in_admingroup}" != "yes" ]
then
echo "WARNING: Adding user "${username}" to group ${_admingroup} failed!"
echo "Please add "${username}" to group ${_admingroup} before"
echo "starting the sshd service!"
echo
fi
fi
if [ ! -x /usr/bin/editrights ]; then
echo "WARNING: The editrights program cannot be found or is not executable."
echo" Unable to insure that ${username} has the appropriate privileges."
else
editrights -a SeAssignPrimaryTokenPrivilege -u "${username}" &&
editrights -a SeCreateTokenPrivilege -u "${username}" &&
editrights -a SeDenyInteractiveLogonRight -u "${username}" &&
editrights -a SeDenyNetworkLogonRight -u "${username}" &&
editrights -a SeDenyRemoteInteractiveLogonRight -u "${username}" &&
editrights -a SeIncreaseQuotaPrivilege -u "${username}" &&
editrights -a SeServiceLogonRight -u "${username}" &&
username_got_all_rights="yes"
if [ "${username_got_all_rights}" != "yes" ]
then
echo
echo "Assigning the appropriate privileges to user '${username}' failed!"
echo "Can't create sshd service!"
exit 1
fi
fi
grep -Eiq "\^${username}\:" ${SYSCONFDIR}/passwd && username_in_passwd=yes
if [ "${username_in_passwd}" != "yes" ]
then
mkpasswd -l -u "${username}" | sed -e 's/bash$/false/' >> ${SYSCONFDIR}/passwd
fi
}
# ======================================================================
# Routine: install_service
# Install cron as a service.
# Start the service or start cron as a job.
# ======================================================================
install_service() {
service="job"
servtest=""
# The default is valid for jobs
username="$USER"
if [ -z "$nt" ]; then
cronregpath='\HKLM\Software\Microsoft\Windows\CurrentVersion\RunServices\Cron'
cronregdata=`regtool -q get $cronregpath`
elif [ ! -e /usr/bin/cygrunsrv.exe ]; then
echo "INFO: Download cygrunsrv to start the mailer daemon as a service."
servtest=no
else
cygrunsrv -Q cron > /dev/null 2>&1
servtest=$?
fi
if [ -z "$nt" -a -n "$cronregdata" ]; then
echo "Cron is already installed as a service."
echo " Key: $cronregpath"
echo " Value: $cronregdata".
if request "Do you want to remove or reinstall it?"; then
if regtool unset "$cronregpath"; then
echo "OK. The cron service was removed."
echo
fi
else
servtest=no
fi
elif [ -n "$nt" -a "${servtest}" = "0" ]; then
echo "Cron is already installed as a service."
if request "Do you want to remove or reinstall it?"; then
if cygrunsrv -R cron; then
echo "OK. The cron service was removed."
echo
fi
else
cronuser="/proc/registry/HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/cron/ObjectName"
username="$(sed 's/.*\\\|LocalSystem//' "${cronuser}")"
service="service"
servtest=no
fi
fi
if [ "${servtest}" = "no" ]; then
true
elif request "Do you want to install the cron daemon as a service?"; then
if [ -n "$nt" ]; then
echo "The service can run either as yourself or under a privileged account."
echo "Running as yourself allows better access to network drives,"
echo "but does not allow to run the crontab of other users."
if [ -n "$nt2003" ]; then
echo "On Windows2003 the SYSTEM account does not allow to setuid to other users."
echo "You will need to have or to create a privileged account."
echo "This script will help you do so."
fi
if request "Do you want to the cron daemon to run as yourself?"; then
if [ -x /usr/bin/editrights ] && editrights -a SeServiceLogonRight -u "${username}"; then
true
else
echo "WARNING: Make sure you have the privilege to logon as a service."
fi
else
echo
username=""
[ -n "$nt2003" ] && create_user
fi
echo
ntsec="nontsec"
if request "Do you want the daemon to run with ntsec?"
then
ntsec="ntsec"
fi
echo
if [ "$username" ]
then
if [ -z "${_password}" ]
then
getvalue "Please enter the password for user '$username'."
_password="${value}"
fi
if cygrunsrv -I cron -p /usr/sbin/cron -e CYGWIN="${ntsec}" \
-a "-D" -d "Cron daemon" -u "$username" -w "$_password"
then
service="service"
else
service="off"
fi
else
if cygrunsrv -I cron -p /usr/sbin/cron -e CYGWIN="${ntsec}" \
-a "-D" -d "Cron daemon"
then
service="service"
else
service="off"
fi
fi
else
cronregdata="\"`cygpath -wl /usr/sbin/cron`\""
if regtool set "$cronregpath" "$cronregdata"
then
echo "OK. The registry was set:"
echo " Key: $cronregpath"
echo " Value: $cronregdata".
echo " This will only take effect at the next reboot."
fi
fi
fi
echo
#############################
# Run diagnostic
#############################
if ! cron_diagnose; then
echo "There are serious issues with your environment."
echo "You should look into them and run this script again."
request "Do you want to continue anyway? " || exit 1
fi
#############################
# Start the daemon?
#############################
if ps -es | grep -Fqi '/usr/sbin/cron'; then
echo "INFO: An cron daemon is already running."
elif [ "${service}" != "off" ]; then
for file in /var/run/cron.pid /var/log/cron.log ; do
chown "${USER}" "${file}" 1> /dev/null 2>&1
rm -f "${file}"
if [ -f "${file}" ]; then
echo "WARNING: ${file} could not be deleted."
echo " Make sure the daemon can write to it."
echo
fi
done
if request "Do you want to start the cron daemon as a ${service} now?"; then
if [ "${service}" = "job" ]; then
/usr/sbin/cron
else
cygrunsrv -S cron
fi
[ $? -eq 0 ] && echo "OK. The cron daemon is now running."
elif [ "${service}" = "job" ]; then
echo "OK. Type '/usr/sbin/cron' to start the cron daemon job."
elif [ -n "$nt" ]; then
echo "OK. Type 'cygrunsrv -S cron' to start the cron daemon service."
echo " It will restart automatically at each reboot."
fi
fi
echo
echo "In case of problem, examine the log file for cron, /var/log/cron.log,"
if [ -n "$nt" ]; then
echo "and the Windows event log"
else
echo "and the Cygwin syslog"
fi
echo "for information about the problem cron is having."
echo
echo "If you cannot fix the problem, then report it to cygwin@cygwin.com."
echo "Please include a copy of your crontab, ('crontab -l')"
echo "and the output of 'cygcheck -srv > cygcheck.txt'."
echo
echo "Please include the generated file 'cygcheck.txt' *as an attachment*,"
echo "and NOT in the body of the mail message."
} # === End of install_service() === #
# Entry point:
# Set PATH to use the Cygwin programs
PATH=/usr/bin:/bin:$PATH
USER="$(id -un)"
sanity_check || return 1
get_NT
install_service
exit $?
-------------- next part --------------
--
Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple
Problem reports: http://cygwin.com/problems.html
Documentation: http://cygwin.com/docs.html
FAQ: http://cygwin.com/faq/
More information about the Cygwin
mailing list