xref: /netbsd-src/external/ibm-public/postfix/dist/conf/postmulti-script (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
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