#! /bin/sh umask 022 # postmulti(1) contract: # # Arguments: # postmulti-script -e # # 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