Home | History | Annotate | Line # | Download | only in conf
      1 #! /bin/sh
      2 #	$NetBSD: postmulti-script,v 1.3 2022/10/08 16:12:43 christos Exp $
      3 #
      4 
      5 umask 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 
     40 USAGE="$0 -e create|destroy|import|deport|enable|disable|assign|init"
     41 usage() { echo "$0: Error: Usage: $USAGE" >&2; exit 1; }
     42 
     43 TAG="$MAIL_LOGTAG/postmulti-script"
     44 fatal() { postlog -p fatal -t "$TAG" "$1"; exit 1; }
     45 
     46 # args: add|del $dir
     47 #
     48 update_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 
     73 assign_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 
     91 BACKUP_IFS="$IFS"
     92 action=
     93 
     94 while getopts ":e:" opt
     95 do
     96     case $opt in
     97     e) action="$OPTARG";;
     98     *) usage;;
     99     esac
    100 done
    101 shift `expr $OPTIND - 1`
    102 
    103 # Check for valid action and required instance name
    104 case "$action" in
    105  create|import|destroy|deport|enable|disable|assign|init) ;;
    106 						       *) usage;;
    107 esac
    108 test $# -eq 0 || usage
    109 
    110 case $action in
    111 init)
    112     postconf -e \
    113     	'multi_instance_wrapper = ${command_directory}/postmulti -p --' \
    114     	'multi_instance_enable = yes'
    115     exit $? ;;
    116 esac
    117 
    118 # Backport note: "-x" requires 2.10 or later, and is not essential here.
    119 #
    120 wrapper=`postconf -hx multi_instance_wrapper` || exit 1
    121 enable=`postconf -hx multi_instance_enable` || exit 1
    122 
    123 test -n "$wrapper" ||
    124     fatal "multi_instance_wrapper is empty, run 'postmulti -e init' first."
    125 
    126 test "$enable" = "yes" ||
    127     fatal "multi_instance_enable!=yes, run 'postmulti -e init' first."
    128 
    129 : ${config_directory:?"Invalid empty target instance config_directory"}
    130 
    131 case $action in
    132 create|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 
    237 deport)
    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 
    254 destroy)
    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 
    304 enable)
    305     postconf -c "$config_directory" -e \
    306     	"multi_instance_enable = yes" || exit 1;;
    307 disable)
    308     postconf -c "$config_directory" -e \
    309     	"multi_instance_enable = no" || exit 1;;
    310 assign)
    311     assign_names || exit 1;;
    312 esac
    313 
    314 exit 0
    315