#!/bin/sh

#++
# NAME
#	postfix-script 1
# SUMMARY
#	execute Postfix administrative commands
# SYNOPSIS
#	\fBpostfix-script\fR \fIcommand\fR
# DESCRIPTION
#	The \fBpostfix-script\fR script executes Postfix administrative
#	commands in an environment that is set up by the \fBpostfix\fR(1)
#	command.
# SEE ALSO
#	master(8) Postfix master program
#	postfix(1) Postfix administrative interface
# LICENSE
# .ad
# .fi
#	The Secure Mailer license must be distributed with this software.
# AUTHOR(S)
#	Wietse Venema
#	IBM T.J. Watson Research
#	P.O. Box 704
#	Yorktown Heights, NY 10598, USA
#--

# Avoid POSIX death due to SIGHUP when some parent process exits.

trap '' 1

case $global_config_directory in
"") echo "This script must be run via the postfix(1) command." 1>&2
    echo "Do not run directly." 1>&2
    exit 1
esac

LOGGER="$command_directory/postlog -t $MAIL_LOGTAG/postfix-script"
INFO="$LOGGER -p info"
WARN="$LOGGER -p warn"
ERROR="$LOGGER -p error"
FATAL="$LOGGER -p fatal"
PANIC="$LOGGER -p panic"
CONF="$command_directory/postconf"
PFIX="$command_directory/postfix"
SCRIPT="$global_config_directory/postfix-script"

umask 022
SHELL=/bin/sh

#
# Can't do much without these in place.
#
cd $command_directory || {
	$FATAL no Postfix command directory $command_directory!
	exit 1
}
cd $daemon_directory || {
	$FATAL no Postfix daemon directory $daemon_directory!
	exit 1
}
test -f master || {
	$FATAL no Postfix master program $daemon_directory/master!
	exit 1
}
cd $config_directory || {
	$FATAL no Postfix configuration directory $config_directory!
	exit 1
}
cd $global_config_directory || {
	$FATAL no Postfix configuration directory $global_config_directory!
	exit 1
}
cd $queue_directory || {
	$FATAL no Postfix queue directory $queue_directory!
	exit 1
}

am_default_instance() {
	test "X$config_directory" = "X`$CONF -dh config_directory`"
}

slave_dirs() {
	dirlistvar=$1; shift

	eval "$dirlistvar="

	# Split list on commas and whitespace...
	OIFS="$IFS"; IFS="$OIFS,"
	set -- $slave_config_directories
	IFS="$OIFS"

	for dir in "$@"
	do
		case $dir in
		/*) : good ;;
		*)  $WARN "Skipping instance '$dir': not an absolute path"
		    continue;;
		esac

		# Is there a Postfix there?
		[ -f "$dir/main.cf" ] || {
			$WARN "Skipping instance '$dir': no main.cf file"
			continue
		}
		[ -f "$dir/master.cf" ] || {
			$WARN "Skipping instance '$dir': no master.cf file"
			continue
		}

		eval "$dirlistvar=\"\${$dirlistvar}\"' $dir'"
	done
}

# Some commands (e.g. start/stop) are automatically applied to
# multiple configuration directories. We want locate the valid
# secondary instances up-front, so as to reduce code duplication
# below. If any secondary instances are not in working order, we
# still process as much as possible.

sdirs=
slave_instance() { return 0; }

if test -n "$slave_config_directories"; then
	case $1 in
	start|check|reload) slave_dirs sdirs;;
	quick-stop|stop|abort|drain) slave_dirs sdirs;;
	esac
fi

if test -n "$sdirs"; then
	slave_instance() {
		err=0
		for dir in $sdirs; do
			$PFIX -1c $dir "$@" || err=$?
		done
		return $err
	}
fi

# Some commands are only valid for enabled Postfix instances
#
if [ "$disable_start" = "yes" ]; then
	case $1 in
	start|disable)
		$FATAL the Postfix mail system is not enabled
		exit 1;;
	esac
fi

# Some commands are only valid when Postfix is stopped.
#
case $1 in
start|disable)
	$daemon_directory/master -t 2>/dev/null || {
		$FATAL cannot $1 Postfix, the mail system is running
		exit 1
	}
	;;
esac

#
# Parse JCL
#
case $1 in

start_msg)

	echo "Start postfix"
	;;

stop_msg)

	echo "Stop postfix"
	;;

enable)
	slave_config_directories="" $SCRIPT check || {
		$FATAL Postfix integrity check failed!
		exit 1
	}

	if [ "$disable_start" = "yes" ]; then
		$INFO enabling the Postfix mail system
		$CONF -e "disable_start = no"
	fi
	;;

disable)
	if [ "$disable_start" = "no" ]; then
		$INFO disabling the Postfix mail system
		$CONF -e "disable_start = yes"
	fi
	;;

start)
	if [ -f $queue_directory/quick-start ]
	then
		rm -f $queue_directory/quick-start
	else
		# Checking all instances up-front complicates the code
		# or checks all non-default instances twice. We optimize
		# for success, and try to ensure that the default instance
		# runs even if the other instances do not. This will ensure
		# that system error notices can leave the system...
		#
		$SCRIPT check-fatal || {
			$FATAL Postfix integrity check failed!
			exit 1
		}
		# Foreground this so it can be stopped. All inodes are cached.
		$SCRIPT check-warn
	fi

	# Some commands are only valid for enabled Postfix instances
	#
	if [ "$disable_start" = "no" ]; then
		$INFO starting the Postfix mail system
		$daemon_directory/master &
		err=0
	else
		$FATAL the Postfix mail system is not enabled
		err=1
	fi

	slave_instance start || err=1
	exit $err
	;;

quick-stop|stop|abort|drain)
	# Stop secondary instances. Finally stop primary.
	slave_instance $1; err=$?
	# Fatal if not running.
	$daemon_directory/master -t 2>/dev/null && {
		$FATAL cannot $1 Postfix, the mail system is not running
		exit 1
	}
	$INFO stopping the Postfix mail system
	case $1 in
		drain) kill -9 `sed 1q pid/master.pid` || err=$?;;
		*)     kill `sed 1q pid/master.pid` || err=$?;;
	esac
	case $1 in quick-stop) touch $queue_directory/quick-start;; esac
	exit $err
	;;

reload)
	$daemon_directory/master -t 2>/dev/null && {
		$FATAL cannot reload Postfix, the mail system is not running
		exit 1
	}
	$INFO refreshing the Postfix mail system
	$command_directory/postsuper active || exit 1
	kill -HUP `sed 1q pid/master.pid`
	$command_directory/postsuper &
	slave_instance reload
	;;

flush)
	$daemon_directory/master -t 2>/dev/null && {
		$FATAL cannot flush Postfix, the mail system is not running
		exit 1
	}
	$command_directory/postqueue -f
	;;

check)
	$SCRIPT check-fatal || exit 1
	slave_instance check-fatal || exit 1
	$SCRIPT check-warn
	slave_instance check-warn
	exit 0
	;;

check-fatal)
	# This command is NOT part of the public interface.

	$SHELL $global_config_directory/post-install create-missing || {
		$WARN unable to create missing queue directories
		exit 1
	}

	# Look for incomplete installations.

	test -f $config_directory/master.cf || {
		$FATAL no $config_directory/master.cf file found
		exit 1
	}

	# See if all queue files are in the right place. This is slow.
	# We must scan all queues for mis-named queue files before the
	# mail system can run.

	$command_directory/postsuper || exit 1
	exit 0
	;;

check-warn)
	# This command is NOT part of the public interface.

	# XXX: Only check the global files in the context of the
	# default instance. Since the default instance handles local
	# mail submission, we expect/require that at least this instance
	# is active. Otherwise, we would be forced to check and report
	# issues with the global directory multiple times...
	#
	if am_default_instance
	then
	    set -- "$config_directory" "$global_config_directory"

	    # Don't check both if either is same as or subdirectory of the other.
	    case $config_directory in
		"$global_config_directory"|"$global_config_directory"/*)
		    set -- "$global_config_directory";;
	    esac
	    #
	    case $global_config_directory in
		"$config_directory"/*)
		    set -- "$config_directory";;
	    esac
	else
	    set -- "$config_directory"
	fi

	for dir in "$@" "$daemon_directory"
	do
	    find "$dir/." ! -user root \
	    	-exec $WARN not owned by root: {} \;
	    find "$dir/." \( -perm -020 -o -perm -002 \) -type f \
		-exec $WARN group or other writable: {} \;
	done

	for dir in "$queue_directory"
	do
	    find "$dir/." -prune \
	    	! -user root -exec $WARN not owned by root: {} \;
	done

	find `ls -d $queue_directory/* | \
	    egrep '/(incoming|active|defer|deferred|bounce|hold|trace|corrupt|public|private|flush)$'` \
	    ! \( -type p -o -type s \) ! -user $mail_owner \
		-exec $WARN not owned by $mail_owner: {} \;

	find $queue_directory/public $queue_directory/maildrop \
	    $command_directory/postqueue $command_directory/postdrop \
	    -prune ! -group $setgid_group \
	    -exec $WARN not owned by group $setgid_group: {} \;

	find $command_directory/postqueue $command_directory/postdrop \
	    -prune ! -perm -02111 \
	    -exec $WARN not set-gid or not owner+group+world executable: {} \;

	for name in `ls -d $queue_directory/* | \
	    egrep '/(bin|etc|lib|usr)$'` ; \
	do \
	    find $name ! -user root \
		-exec $WARN not owned by root: {} \; ; \
	done

	# WARNING: this should not descend into the maildrop directory.
	# maildrop is the least trusted Postfix directory.

	find $queue_directory/maildrop/. -prune ! -user $mail_owner \
	    -exec $WARN not owned by $mail_owner: $queue_directory/maildrop \;

	for dir in bin etc lib sbin usr
	do
		test -d $dir && find $dir -type f -print | while read path
		do
			test -f /$path && {
			    cmp -s $path /$path || 
				$WARN $queue_directory/$path and /$path differ
			}
		done
	done

	find corrupt -type f -exec $WARN damaged message: {} \;

	# XXX also: look for weird stuff, weird permissions, etc.

	test -f /usr/sbin/sendmail -a -f /usr/lib/sendmail && {
	    cmp -s /usr/sbin/sendmail /usr/lib/sendmail || {
		$WARN /usr/lib/sendmail and /usr/sbin/sendmail differ
		$WARN Replace one by a symbolic link to the other
	    }
	}
	exit $err
	;;

set-permissions|upgrade-configuration)
	# 2.1 compatibility. Should really use post-install ...
	exec $global_config_directory/post-install create-missing "$@"
	;;

post-install)
	# Sub commands documented as post-install(1)
	shift
	exec $global_config_directory/post-install "$@"
	;;

/*)
	# Currently not part of the public interface.
	"$@"
	;;

*)
	$FATAL "usage: postfix start (or stop, reload, abort, flush, check, enable, disable or post-install (see post-install(1) for details))."
	exit 1
	;;

esac
