1#! /bin/sh 2# $NetBSD: postmulti-script,v 1.3 2022/10/08 16:12:43 christos Exp $ 3# 4 5umask 022 6 7# postmulti(1) contract: 8# 9# Arguments: 10# postmulti-script -e <edit_command> 11# 12# Environment: 13# 14# All actions: 15# 16# MAIL_CONFIG - config_directory of primary instance 17# command_directory - From primary instance 18# daemon_directory - From primary instance 19# meta_directory - From primary instance 20# shlib_directory - From primary instance 21# config_directory - config_directory of target instance 22# queue_directory - queue_directory of target instance 23# data_directory - data_directory of target instance 24# 25# Create, destroy, import and deport: 26# 27# multi_instance_directories - New value for primary instance 28# 29# Create, import and assign (unset == nochange, "-" == clear): 30# 31# multi_instance_group - New value for target instance 32# multi_instance_name - New value for target instance 33 34: ${MAIL_CONFIG:?"do not invoke this command directly"} 35: ${command_directory:?"do not invoke this command directly"} 36: ${daemon_directory:?"do not invoke this command directly"} 37: ${meta_directory:?"do not invoke this command directly"} 38: ${shlib_directory:?"do not invoke this command directly"} 39 40USAGE="$0 -e create|destroy|import|deport|enable|disable|assign|init" 41usage() { echo "$0: Error: Usage: $USAGE" >&2; exit 1; } 42 43TAG="$MAIL_LOGTAG/postmulti-script" 44fatal() { postlog -p fatal -t "$TAG" "$1"; exit 1; } 45 46# args: add|del $dir 47# 48update_cfdirs() { 49 op=$1 50 dir=$2 51 52 alt=`postconf -h alternate_config_directories` || return 1 53 54 shift $# # Needed on SunOS where bare "set --" is NOP! 55 IFS="$IFS,"; set -- $alt; IFS="$BACKUP_IFS" 56 keep= 57 found= 58 # Portability: SunOS "sh" needs 'in "$@"' for one-line for-loop. 59 for d in "$@"; do [ "$d" = "$dir" ] && found=1 || keep="$keep $d"; done 60 61 set -- "multi_instance_directories = $multi_instance_directories" 62 63 case $op in 64 add) test -n "$found" || 65 set -- "$@" "alternate_config_directories =$keep $dir";; 66 del) test -n "$found" && 67 set -- "$@" "alternate_config_directories =$keep";; 68 *) return 1;; # XXX: Internal error 69 esac 70 postconf -e "$@" || return 1 71} 72 73assign_names() { 74 # Set the instance name and group 75 # 76 test -n "$multi_instance_name" && { 77 test "$multi_instance_name" = "-" && multi_instance_name= 78 set -- "$@" "multi_instance_name = $multi_instance_name" 79 } 80 test -n "$multi_instance_group" && { 81 test "$multi_instance_group" = "-" && multi_instance_group= 82 set -- "$@" "multi_instance_group = $multi_instance_group" 83 } 84 test $# -eq 0 || postconf -c "$config_directory" -e "$@" || return 1 85} 86 87# Process command-line options and parameter settings. Work around 88# brain damaged shells. "IFS=value command" should not make the 89# IFS=value setting permanent. But some broken standard allows it. 90 91BACKUP_IFS="$IFS" 92action= 93 94while getopts ":e:" opt 95do 96 case $opt in 97 e) action="$OPTARG";; 98 *) usage;; 99 esac 100done 101shift `expr $OPTIND - 1` 102 103# Check for valid action and required instance name 104case "$action" in 105 create|import|destroy|deport|enable|disable|assign|init) ;; 106 *) usage;; 107esac 108test $# -eq 0 || usage 109 110case $action in 111init) 112 postconf -e \ 113 'multi_instance_wrapper = ${command_directory}/postmulti -p --' \ 114 'multi_instance_enable = yes' 115 exit $? ;; 116esac 117 118# Backport note: "-x" requires 2.10 or later, and is not essential here. 119# 120wrapper=`postconf -hx multi_instance_wrapper` || exit 1 121enable=`postconf -hx multi_instance_enable` || exit 1 122 123test -n "$wrapper" || 124 fatal "multi_instance_wrapper is empty, run 'postmulti -e init' first." 125 126test "$enable" = "yes" || 127 fatal "multi_instance_enable!=yes, run 'postmulti -e init' first." 128 129: ${config_directory:?"Invalid empty target instance config_directory"} 130 131case $action in 132create|import) 133 134 # Atomically install stock main.cf/master.cf files. We install the 135 # master.cf file last. Once it is present the instance is complete. 136 # 137 test -f $config_directory/main.cf -a \ 138 -f $config_directory/master.cf || { 139 140 test "$action" = "create" || { 141 test -f $config_directory/main.cf || 142 fatal "'$config_directory' lacks a main.cf file" 143 test -f $config_directory/master.cf || 144 fatal "'$config_directory' lacks a master.cf file" 145 } 146 147 test -f $meta_directory/main.cf.proto || 148 fatal "Missing main.cf prototype: $meta_directory/main.cf.proto" 149 test -f $meta_directory/master.cf.proto || 150 fatal "Missing master.cf prototype: $meta_directory/master.cf.proto" 151 152 # Create instance-specific directories 153 # 154 test -d $config_directory || 155 { (umask 022; mkdir -p $config_directory) || exit 1; } 156 test -d $queue_directory || 157 { (umask 022; mkdir -p $queue_directory) || exit 1; } 158 test -d $data_directory || 159 { (umask 077; mkdir -p $data_directory) || exit 1; } 160 161 tmpdir=$config_directory/.tmp 162 (umask 077; mkdir -p $tmpdir) || exit 1 163 cp -p $meta_directory/main.cf.proto $tmpdir/main.cf || exit 1 164 165 # Shared install parameters are cloned from user-specified values in 166 # the default instance, but only if explicitly set there. Otherwise, 167 # they are commented out in the new main.cf file. 168 # 169 SHARED_PARAMETERS=" 170 command_directory 171 daemon_directory 172 meta_directory 173 mail_owner 174 setgid_group 175 sendmail_path 176 mailq_path 177 newaliases_path 178 html_directory 179 manpage_directory 180 sample_directory 181 readme_directory 182 shlib_directory 183 " 184 185 shift $# # Needed on SunOS where bare "set --" is NOP! 186 comment_out= 187 for p in $SHARED_PARAMETERS; do 188 val=`postconf -nh $p` || exit 1 189 test -n "$val" && { set -- "$@" "$p = $val"; continue; } 190 comment_out="$comment_out $p" 191 done 192 193 # First comment-out any parameters that take default values 194 test -n "$comment_out" && { 195 postconf -c $tmpdir -# $comment_out || exit 1 196 } 197 198 # Now add instance-specific and non-default values. 199 # By default, disable inet services and local submission 200 # in new instances 201 # 202 postconf -c $tmpdir -e \ 203 "queue_directory = $queue_directory" \ 204 "data_directory = $data_directory" \ 205 "authorized_submit_users =" \ 206 "master_service_disable = inet" \ 207 "$@" || exit 1 208 209 210 cp -p $meta_directory/master.cf.proto $tmpdir/master.cf || exit 1 211 mv $tmpdir/main.cf $config_directory/main.cf || exit 1 212 mv $tmpdir/master.cf $config_directory/master.cf || exit 1 213 rmdir $tmpdir 2>/dev/null 214 } 215 216 # Set instance name and group 217 # 218 assign_names || exit 1 219 220 # Update multi_instance_directories 221 # and drop from alternate_config_directories 222 # 223 # XXX: Must happen before set-permissions below, otherwise instance 224 # is treated as an independent instance by post-install via postfix(1). 225 # 226 update_cfdirs del $config_directory || exit 1 227 228 # Update permissions of private files. Verifies existence of 229 # queue_directory and data_directory, ... 230 # 231 # XXX: Must happen after instance list updates above, otherwise instance 232 # is treated as an independent instance by post-install via postfix(1). 233 # 234 postfix -c $config_directory set-permissions || exit 1 235 ;; 236 237deport) 238 # Deporting an already deleted instance? 239 # 240 [ -f "$config_directory/main.cf" ] || { 241 update_cfdirs del $config_directory 242 exit $? 243 } 244 245 postfix -c "$config_directory" status >/dev/null 2>&1 && 246 fatal "Instance '$config_directory' is not stopped" 247 248 # Update multi_instance_directories 249 # and add to alternate_config_directories 250 # 251 update_cfdirs add $config_directory || exit 1 252 ;; 253 254destroy) 255 256 # "postmulti -e destroy" will remove an entire instance only when 257 # invoked immediately after "postmulti -e create" (i.e. before 258 # other files are added to the instance). We delete only known 259 # safe names without "/". 260 # 261 QUEUE_SUBDIRS="active bounce corrupt defer deferred flush hold \ 262 incoming maildrop pid private public saved trace" 263 #DEBUG=echo 264 WARN="postlog -p warn -t $TAG" 265 266 # Locate the target instance 267 # 268 [ -f "$config_directory/main.cf" ] || 269 fatal "$config_directory/main.cf file not found" 270 271 postfix -c "$config_directory" status >/dev/null 2>&1 && 272 fatal "Instance '$config_directory' is not stopped" 273 274 # Update multi_instance directories 275 # and also (just in case) drop from alternate_config_directories 276 # 277 $DEBUG update_cfdirs del "$config_directory" || exit 1 278 279 # XXX: Internal "postfix /some/cmd" interface. 280 # 281 postfix -c "$config_directory" /bin/sh -c " 282 for q in $QUEUE_SUBDIRS 283 do 284 $DEBUG rmdir -- \$q || 285 $WARN \`pwd\`/\$q: please verify contents and remove by hand 286 done 287 " 288 289 postfix -c "$config_directory" /bin/sh -c " 290 for dir in \$data_directory \$queue_directory 291 do 292 $DEBUG rmdir -- \$dir || 293 $WARN \$dir: please verify contents and remove by hand 294 done 295 " 296 297 # In the configuration directory remove just the main.cf and master.cf 298 # files. 299 $DEBUG rm -f -- "$config_directory/master.cf" "$config_directory/main.cf" 2>/dev/null 300 $DEBUG rmdir -- "$config_directory" || 301 $WARN $config_directory: please verify contents and remove by hand 302 ;; 303 304enable) 305 postconf -c "$config_directory" -e \ 306 "multi_instance_enable = yes" || exit 1;; 307disable) 308 postconf -c "$config_directory" -e \ 309 "multi_instance_enable = no" || exit 1;; 310assign) 311 assign_names || exit 1;; 312esac 313 314exit 0 315