312 lines
9.1 KiB
Bash
312 lines
9.1 KiB
Bash
#! /bin/sh
|
|
|
|
umask 022
|
|
|
|
# postmulti(1) contract:
|
|
#
|
|
# Arguments:
|
|
# postmulti-script -e <edit_command>
|
|
#
|
|
# Environment:
|
|
#
|
|
# All actions:
|
|
#
|
|
# MAIL_CONFIG - config_directory of primary instance
|
|
# command_directory - From primary instance
|
|
# daemon_directory - From primary instance
|
|
# meta_directory - From primary instance
|
|
# shlib_directory - From primary instance
|
|
# config_directory - config_directory of target instance
|
|
# queue_directory - queue_directory of target instance
|
|
# data_directory - data_directory of target instance
|
|
#
|
|
# Create, destroy, import and deport:
|
|
#
|
|
# multi_instance_directories - New value for primary instance
|
|
#
|
|
# Create, import and assign (unset == nochange, "-" == clear):
|
|
#
|
|
# multi_instance_group - New value for target instance
|
|
# multi_instance_name - New value for target instance
|
|
|
|
: ${MAIL_CONFIG:?"do not invoke this command directly"}
|
|
: ${command_directory:?"do not invoke this command directly"}
|
|
: ${daemon_directory:?"do not invoke this command directly"}
|
|
: ${meta_directory:?"do not invoke this command directly"}
|
|
: ${shlib_directory:?"do not invoke this command directly"}
|
|
|
|
USAGE="$0 -e create|destroy|import|deport|enable|disable|assign|init"
|
|
usage() { echo "$0: Error: Usage: $USAGE" >&2; exit 1; }
|
|
|
|
TAG="$MAIL_LOGTAG/postmulti-script"
|
|
fatal() { postlog -p fatal -t "$TAG" "$1"; exit 1; }
|
|
|
|
# args: add|del $dir
|
|
#
|
|
update_cfdirs() {
|
|
op=$1
|
|
dir=$2
|
|
|
|
alt=`postconf -h alternate_config_directories` || return 1
|
|
|
|
shift $# # Needed on SunOS where bare "set --" is NOP!
|
|
IFS="$IFS,"; set -- $alt; IFS="$BACKUP_IFS"
|
|
keep=
|
|
found=
|
|
# Portability: SunOS "sh" needs 'in "$@"' for one-line for-loop.
|
|
for d in "$@"; do [ "$d" = "$dir" ] && found=1 || keep="$keep $d"; done
|
|
|
|
set -- "multi_instance_directories = $multi_instance_directories"
|
|
|
|
case $op in
|
|
add) test -n "$found" ||
|
|
set -- "$@" "alternate_config_directories =$keep $dir";;
|
|
del) test -n "$found" &&
|
|
set -- "$@" "alternate_config_directories =$keep";;
|
|
*) return 1;; # XXX: Internal error
|
|
esac
|
|
postconf -e "$@" || return 1
|
|
}
|
|
|
|
assign_names() {
|
|
# Set the instance name and group
|
|
#
|
|
test -n "$multi_instance_name" && {
|
|
test "$multi_instance_name" = "-" && multi_instance_name=
|
|
set -- "$@" "multi_instance_name = $multi_instance_name"
|
|
}
|
|
test -n "$multi_instance_group" && {
|
|
test "$multi_instance_group" = "-" && multi_instance_group=
|
|
set -- "$@" "multi_instance_group = $multi_instance_group"
|
|
}
|
|
test $# -eq 0 || postconf -c "$config_directory" -e "$@" || return 1
|
|
}
|
|
|
|
# Process command-line options and parameter settings. Work around
|
|
# brain damaged shells. "IFS=value command" should not make the
|
|
# IFS=value setting permanent. But some broken standard allows it.
|
|
|
|
BACKUP_IFS="$IFS"
|
|
action=
|
|
|
|
while getopts ":e:" opt
|
|
do
|
|
case $opt in
|
|
e) action="$OPTARG";;
|
|
*) usage;;
|
|
esac
|
|
done
|
|
shift `expr $OPTIND - 1`
|
|
|
|
# Check for valid action and required instance name
|
|
case "$action" in
|
|
create|import|destroy|deport|enable|disable|assign|init) ;;
|
|
*) usage;;
|
|
esac
|
|
test $# -eq 0 || usage
|
|
|
|
case $action in
|
|
init)
|
|
postconf -e \
|
|
'multi_instance_wrapper = ${command_directory}/postmulti -p --' \
|
|
'multi_instance_enable = yes'
|
|
exit $? ;;
|
|
esac
|
|
|
|
# Backport note: "-x" requires 2.10 or later, and is not essential here.
|
|
#
|
|
wrapper=`postconf -hx multi_instance_wrapper` || exit 1
|
|
enable=`postconf -hx multi_instance_enable` || exit 1
|
|
|
|
test -n "$wrapper" ||
|
|
fatal "multi_instance_wrapper is empty, run 'postmulti -e init' first."
|
|
|
|
test "$enable" = "yes" ||
|
|
fatal "multi_instance_enable!=yes, run 'postmulti -e init' first."
|
|
|
|
: ${config_directory:?"Invalid empty target instance config_directory"}
|
|
|
|
case $action in
|
|
create|import)
|
|
|
|
# Atomically install stock main.cf/master.cf files. We install the
|
|
# master.cf file last. Once it is present the instance is complete.
|
|
#
|
|
test -f $config_directory/main.cf -a \
|
|
-f $config_directory/master.cf || {
|
|
|
|
test "$action" = "create" || {
|
|
test -f $config_directory/main.cf ||
|
|
fatal "'$config_directory' lacks a main.cf file"
|
|
test -f $config_directory/master.cf ||
|
|
fatal "'$config_directory' lacks a master.cf file"
|
|
}
|
|
|
|
test -f $meta_directory/main.cf.proto ||
|
|
fatal "Missing main.cf prototype: $meta_directory/main.cf.proto"
|
|
test -f $meta_directory/master.cf.proto ||
|
|
fatal "Missing master.cf prototype: $meta_directory/master.cf.proto"
|
|
|
|
# Create instance-specific directories
|
|
#
|
|
test -d $config_directory ||
|
|
{ (umask 022; mkdir -p $config_directory) || exit 1; }
|
|
test -d $queue_directory ||
|
|
{ (umask 022; mkdir -p $queue_directory) || exit 1; }
|
|
test -d $data_directory ||
|
|
{ (umask 077; mkdir -p $data_directory) || exit 1; }
|
|
|
|
tmpdir=$config_directory/.tmp
|
|
(umask 077; mkdir -p $tmpdir) || exit 1
|
|
cp -p $meta_directory/main.cf.proto $tmpdir/main.cf || exit 1
|
|
|
|
# Shared install parameters are cloned from user-specified values in
|
|
# the default instance, but only if explicitly set there. Otherwise,
|
|
# they are commented out in the new main.cf file.
|
|
#
|
|
SHARED_PARAMETERS="
|
|
command_directory
|
|
daemon_directory
|
|
meta_directory
|
|
mail_owner
|
|
setgid_group
|
|
sendmail_path
|
|
mailq_path
|
|
newaliases_path
|
|
html_directory
|
|
manpage_directory
|
|
sample_directory
|
|
readme_directory
|
|
shlib_directory
|
|
"
|
|
|
|
shift $# # Needed on SunOS where bare "set --" is NOP!
|
|
comment_out=
|
|
for p in $SHARED_PARAMETERS; do
|
|
val=`postconf -nh $p` || exit 1
|
|
test -n "$val" && { set -- "$@" "$p = $val"; continue; }
|
|
comment_out="$comment_out $p"
|
|
done
|
|
|
|
# First comment-out any parameters that take default values
|
|
test -n "$comment_out" && {
|
|
postconf -c $tmpdir -# $comment_out || exit 1
|
|
}
|
|
|
|
# Now add instance-specific and non-default values.
|
|
# By default, disable inet services and local submission
|
|
# in new instances
|
|
#
|
|
postconf -c $tmpdir -e \
|
|
"queue_directory = $queue_directory" \
|
|
"data_directory = $data_directory" \
|
|
"authorized_submit_users =" \
|
|
"master_service_disable = inet" \
|
|
"$@" || exit 1
|
|
|
|
|
|
cp -p $meta_directory/master.cf.proto $tmpdir/master.cf || exit 1
|
|
mv $tmpdir/main.cf $config_directory/main.cf || exit 1
|
|
mv $tmpdir/master.cf $config_directory/master.cf || exit 1
|
|
rmdir $tmpdir 2>/dev/null
|
|
}
|
|
|
|
# Set instance name and group
|
|
#
|
|
assign_names || exit 1
|
|
|
|
# Update multi_instance_directories
|
|
# and drop from alternate_config_directories
|
|
#
|
|
# XXX: Must happen before set-permissions below, otherwise instance
|
|
# is treated as an independent instance by post-install via postfix(1).
|
|
#
|
|
update_cfdirs del $config_directory || exit 1
|
|
|
|
# Update permissions of private files. Verifies existence of
|
|
# queue_directory and data_directory, ...
|
|
#
|
|
# XXX: Must happen after instance list updates above, otherwise instance
|
|
# is treated as an independent instance by post-install via postfix(1).
|
|
#
|
|
postfix -c $config_directory set-permissions || exit 1
|
|
;;
|
|
|
|
deport)
|
|
# Deporting an already deleted instance?
|
|
#
|
|
[ -f "$config_directory/main.cf" ] || {
|
|
update_cfdirs del $config_directory
|
|
exit $?
|
|
}
|
|
|
|
postfix -c "$config_directory" status >/dev/null 2>&1 &&
|
|
fatal "Instance '$config_directory' is not stopped"
|
|
|
|
# Update multi_instance_directories
|
|
# and add to alternate_config_directories
|
|
#
|
|
update_cfdirs add $config_directory || exit 1
|
|
;;
|
|
|
|
destroy)
|
|
|
|
# "postmulti -e destroy" will remove an entire instance only when
|
|
# invoked immediately after "postmulti -e create" (i.e. before
|
|
# other files are added to the instance). We delete only known
|
|
# safe names without "/".
|
|
#
|
|
QUEUE_SUBDIRS="active bounce corrupt defer deferred flush hold \
|
|
incoming maildrop pid private public saved trace"
|
|
#DEBUG=echo
|
|
WARN="postlog -p warn -t $TAG"
|
|
|
|
# Locate the target instance
|
|
#
|
|
[ -f "$config_directory/main.cf" ] ||
|
|
fatal "$config_directory/main.cf file not found"
|
|
|
|
postfix -c "$config_directory" status >/dev/null 2>&1 &&
|
|
fatal "Instance '$config_directory' is not stopped"
|
|
|
|
# Update multi_instance directories
|
|
# and also (just in case) drop from alternate_config_directories
|
|
#
|
|
$DEBUG update_cfdirs del "$config_directory" || exit 1
|
|
|
|
# XXX: Internal "postfix /some/cmd" interface.
|
|
#
|
|
postfix -c "$config_directory" /bin/sh -c "
|
|
for q in $QUEUE_SUBDIRS
|
|
do
|
|
$DEBUG rmdir -- \$q ||
|
|
$WARN \`pwd\`/\$q: please verify contents and remove by hand
|
|
done
|
|
"
|
|
|
|
postfix -c "$config_directory" /bin/sh -c "
|
|
for dir in \$data_directory \$queue_directory
|
|
do
|
|
$DEBUG rmdir -- \$dir ||
|
|
$WARN \$dir: please verify contents and remove by hand
|
|
done
|
|
"
|
|
|
|
# In the configuration directory remove just the main.cf and master.cf
|
|
# files.
|
|
$DEBUG rm -f -- "$config_directory/master.cf" "$config_directory/main.cf" 2>/dev/null
|
|
$DEBUG rmdir -- "$config_directory" ||
|
|
$WARN $config_directory: please verify contents and remove by hand
|
|
;;
|
|
|
|
enable)
|
|
postconf -c "$config_directory" -e \
|
|
"multi_instance_enable = yes" || exit 1;;
|
|
disable)
|
|
postconf -c "$config_directory" -e \
|
|
"multi_instance_enable = no" || exit 1;;
|
|
assign)
|
|
assign_names || exit 1;;
|
|
esac
|
|
|
|
exit 0
|