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