xref: /netbsd-src/external/ibm-public/postfix/dist/src/postmulti/postmulti.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /*	$NetBSD: postmulti.c,v 1.1.1.5 2014/07/06 19:27:54 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	postmulti 1
6 /* SUMMARY
7 /*	Postfix multi-instance manager
8 /* SYNOPSIS
9 /* .fi
10 /*	\fBENABLING MULTI-INSTANCE MANAGEMENT:\fR
11 /*
12 /*	\fBpostmulti\fR \fB-e init\fR [\fB-v\fR]
13 /*
14 /*	\fBITERATOR MODE:\fR
15 /*
16 /*	\fBpostmulti\fR \fB-l\fR [\fB-aRv\fR] [\fB-g \fIgroup\fR]
17 /*	[\fB-i \fIname\fR]
18 /*
19 /*	\fBpostmulti\fR \fB-p\fR [\fB-av\fR] [\fB-g \fIgroup\fR]
20 /*	[\fB-i \fIname\fR] \fIcommand...\fR
21 /*
22 /*	\fBpostmulti\fR \fB-x\fR [\fB-aRv\fR] [\fB-g \fIgroup\fR]
23 /*	[\fB-i \fIname\fR] \fIcommand...\fR
24 /*
25 /*	\fBLIFE-CYCLE MANAGEMENT:\fR
26 /*
27 /*	\fBpostmulti\fR \fB-e create\fR [\fB-av\fR]
28 /*	[\fB-g \fIgroup\fR] [\fB-i \fIname\fR] [\fB-G \fIgroup\fR]
29 /*	[\fB-I \fIname\fR] [\fIparam=value\fR ...]
30 /*
31 /*	\fBpostmulti\fR \fB-e import\fR [\fB-av\fR]
32 /*	[\fB-g \fIgroup\fR] [\fB-i \fIname\fR] [\fB-G \fIgroup\fR]
33 /*	[\fB-I \fIname\fR] [\fBconfig_directory=\fI/path\fR]
34 /*
35 /*	\fBpostmulti\fR \fB-e destroy\fR [\fB-v\fR] \fB-i \fIname\fR
36 /*
37 /*	\fBpostmulti\fR \fB-e deport\fR [\fB-v\fR] \fB-i \fIname\fR
38 /*
39 /*	\fBpostmulti\fR \fB-e enable\fR [\fB-v\fR] \fB-i \fIname\fR
40 /*
41 /*	\fBpostmulti\fR \fB-e disable\fR [\fB-v\fR] \fB-i \fIname\fR
42 /*
43 /*	\fBpostmulti\fR \fB-e assign\fR [\fB-v\fR] \fB-i \fIname\fR
44 /*	[\fB-I \fIname\fR] [-G \fIgroup\fR]
45 /* DESCRIPTION
46 /*	The \fBpostmulti\fR(1) command allows a Postfix administrator
47 /*	to manage multiple Postfix instances on a single host.
48 /*
49 /*	\fBpostmulti\fR(1) implements two fundamental modes of
50 /*	operation.  In \fBiterator\fR mode, it executes the same
51 /*	command for multiple Postfix instances.  In \fBlife-cycle
52 /*	management\fR mode, it adds or deletes one instance, or
53 /*	changes the multi-instance status of one instance.
54 /*
55 /*	Each mode of operation has its own command syntax. For this
56 /*	reason, each mode is documented in separate sections below.
57 /* BACKGROUND
58 /* .ad
59 /* .fi
60 /*	A multi-instance configuration consists of one primary
61 /*	Postfix instance, and one or more secondary instances whose
62 /*	configuration directory pathnames are recorded in the primary
63 /*	instance's main.cf file. Postfix instances share program
64 /*	files and documentation, but have their own configuration,
65 /*	queue and data directories.
66 /*
67 /*	Currently, only the default Postfix instance can be used
68 /*	as primary instance in a multi-instance configuration. The
69 /*	\fBpostmulti\fR(1) command does not currently support a \fB-c\fR
70 /*	option to select an alternative primary instance, and exits
71 /*	with a fatal error if the \fBMAIL_CONFIG\fR environment
72 /*	variable is set to a non-default configuration directory.
73 /*
74 /*	See the MULTI_INSTANCE_README tutorial for a more detailed
75 /*	discussion of multi-instance management with \fBpostmulti\fR(1).
76 /* ITERATOR MODE
77 /* .ad
78 /* .fi
79 /*	In iterator mode, \fBpostmulti\fR performs the same operation
80 /*	on all Postfix instances in turn.
81 /*
82 /*	If multi-instance support is not enabled, the requested
83 /*	command is performed just for the primary instance.
84 /* .PP
85 /*	Iterator mode implements the following command options:
86 /* .SH "Instance selection"
87 /* .IP \fB-a\fR
88 /*	Perform the operation on all instances. This is the default.
89 /* .IP "\fB-g \fIgroup\fR"
90 /*	Perform the operation only for members of the named \fIgroup\fR.
91 /* .IP "\fB-i \fIname\fR"
92 /*	Perform the operation only for the instance with the specified
93 /*	\fIname\fR.  You can specify either the instance name
94 /*	or the absolute pathname of the instance's configuration
95 /*	directory.  Specify "-" to select the primary Postfix instance.
96 /* .IP \fB-R\fR
97 /*	Reverse the iteration order. This may be appropriate when
98 /*	updating a multi-instance system, where "sink" instances
99 /*	are started before "source" instances.
100 /* .sp
101 /*	This option cannot be used with \fB-p\fR.
102 /* .SH "List mode"
103 /* .IP \fB-l\fR
104 /*	List Postfix instances with their instance name, instance
105 /*	group name, enable/disable status and configuration directory.
106 /* .SH "Postfix-wrapper mode"
107 /* .IP \fB-p\fR
108 /*	Invoke \fBpostfix(1)\fR to execute the specified \fIcommand\fR.
109 /*	This option implements the \fBpostfix-wrapper\fR(5) interface.
110 /* .RS
111 /* .IP \(bu
112 /*	With "start"-like commands, "postfix check" is executed for
113 /*	instances that are not enabled. The full list of commands
114 /*	is specified with the postmulti_start_commands parameter.
115 /* .IP \(bu
116 /*	With "stop"-like commands, the iteration order is reversed,
117 /*	and disabled instances are skipped. The full list of commands
118 /*	is specified with the postmulti_stop_commands parameter.
119 /* .IP \(bu
120 /*	With "reload" and other commands that require a started
121 /*	instance, disabled instances are skipped. The full list of
122 /*	commands is specified with the postmulti_control_commands
123 /*	parameter.
124 /* .IP \(bu
125 /*	With "status" and other commands that don't require a started
126 /*	instance, the command is executed for all instances.
127 /* .RE
128 /* .IP
129 /*	The \fB-p\fR option can also be used interactively to
130 /*	start/stop/etc.  a named instance or instance group. For
131 /*	example, to start just the instances in the group "msa",
132 /*	invoke \fBpostmulti\fR(1) as follows:
133 /* .RS
134 /* .IP
135 /*	# postmulti -g msa -p start
136 /* .RE
137 /* .SH "Command mode"
138 /* .IP \fB-x\fR
139 /*	Execute the specified \fIcommand\fR for all Postfix instances.
140 /*	The command runs with appropriate environment settings for
141 /*	MAIL_CONFIG, command_directory, daemon_directory,
142 /*	config_directory, queue_directory, data_directory,
143 /*	multi_instance_name, multi_instance_group and
144 /*	multi_instance_enable.
145 /* .SH "Other options"
146 /* .IP \fB-v\fR
147 /*	Enable verbose logging for debugging purposes. Multiple
148 /*	\fB-v\fR options make the software increasingly verbose.
149 /* LIFE-CYCLE MANAGEMENT MODE
150 /* .ad
151 /* .fi
152 /*	With the \fB-e\fR option \fBpostmulti\fR(1) can be used to
153 /*	add or delete a Postfix instance, and to manage the
154 /*	multi-instance status of an existing instance.
155 /* .PP
156 /*	The following options are implemented:
157 /* .SH "Existing instance selection"
158 /* .IP \fB-a\fR
159 /*	When creating or importing an instance, place the new
160 /*	instance at the front of the secondary instance list.
161 /* .IP "\fB-g \fIgroup\fR"
162 /*	When creating or importing an instance, place the new
163 /*	instance before the first secondary instance that is a
164 /*	member of the specified group.
165 /* .IP "\fB-i \fIname\fR"
166 /*	When creating or importing an instance, place the new
167 /*	instance before the matching secondary instance.
168 /* .sp
169 /*	With other life-cycle operations, apply the operation to
170 /*	the named existing instance.  Specify "-" to select the
171 /*	primary Postfix instance.
172 /* .SH "New or existing instance name assignment"
173 /* .IP "\fB-I \fIname\fR"
174 /*	Assign the specified instance \fIname\fR to an existing
175 /*	instance, newly-created instance, or imported instance.
176 /*	Instance
177 /*	names other than "-" (which makes the instance "nameless")
178 /*	must start with "postfix-".  This restriction reduces the
179 /*	likelihood of name collisions with system files.
180 /* .IP "\fB-G \fIgroup\fR"
181 /*	Assign the specified \fIgroup\fR name to an existing instance
182 /*	or to a newly created or imported instance.
183 /* .SH "Instance creation/deletion/status change"
184 /* .IP "\fB-e \fIaction\fR"
185 /*	"Edit" managed instances. The following actions are supported:
186 /* .RS
187 /* .IP \fBinit\fR
188 /*	This command is required before \fBpostmulti\fR(1) can be
189 /*	used to manage Postfix instances.  The "postmulti -e init"
190 /*	command updates the primary instance's main.cf file by
191 /*	setting:
192 /* .RS
193 /* .IP
194 /* .nf
195 /*	multi_instance_wrapper =
196 /*		${command_directory}/postmulti -p --
197 /*	multi_instance_enable = yes
198 /* .fi
199 /* .RE
200 /* .IP
201 /*	You can set these by other means if you prefer.
202 /* .IP \fBcreate\fR
203 /*	Create a new Postfix instance and add it to the
204 /*	multi_instance_directories parameter of the primary instance.
205 /*	The "\fB-I \fIname\fR" option is recommended to give the
206 /*	instance a short name that is used to construct default
207 /*	values for the private directories of the new instance. The
208 /*	"\fB-G \fIgroup\fR" option may be specified to assign the
209 /*	instance to a group, otherwise, the new instance is not a
210 /*	member of any groups.
211 /* .sp
212 /*	The new instance main.cf is the stock main.cf with the
213 /*	parameters that specify the locations of shared files cloned
214 /*	from the primary instance.  For "nameless" instances, you
215 /*	should manually adjust "syslog_name" to yield a unique
216 /*	"logtag" starting with "postfix-" that will uniquely identify
217 /*	the instance in the mail logs. It is simpler to assign the
218 /*	instance a short name with the "\fB-I \fIname\fR" option.
219 /* .sp
220 /*	Optional "name=value" arguments specify the instance
221 /*	config_directory, queue_directory and data_directory.
222 /*	For example:
223 /* .RS
224 /* .IP
225 /* .nf
226 /*	# postmulti -I postfix-mumble \e
227 /*		-G mygroup -e create \e
228 /*		config_directory=/my/config/dir \e
229 /*		queue_directory=/my/queue/dir \e
230 /*		data_directory=/my/data/dir
231 /* .fi
232 /* .RE
233 /* .IP
234 /*	If any of these pathnames is not supplied, the program
235 /*	attempts to generate the pathname by taking the corresponding
236 /*	primary instance pathname, and by replacing the last pathname
237 /*	component by the value of the \fB-I\fR option.
238 /* .sp
239 /*	If the instance configuration directory already exists, and
240 /*	contains both a main.cf and master.cf file, \fBcreate\fR
241 /*	will "import" the instance as-is. For existing instances,
242 /*	\fBcreate\fR and \fBimport\fR are identical.
243 /* .IP \fBimport\fR
244 /*	Import an existing instance into the list of instances
245 /*	managed by the \fBpostmulti\fR(1) multi-instance manager.
246 /*	This adds the instance to the multi_instance_directories
247 /*	list of the primary instance.  If the "\fB-I \fIname\fR"
248 /*	option is provided it specifies the new name for the instance
249 /*	and is used to define a default location for the instance
250 /*	configuration directory	(as with \fBcreate\fR above).  The
251 /*	"\fB-G \fIgroup\fR" option may be used to assign the instance
252 /*	to a group. Add a "\fBconfig_directory=\fI/path\fR" argument
253 /*	to override a default pathname based on "\fB-I \fIname\fR".
254 /* .IP \fBdestroy\fR
255 /*	Destroy a secondary Postfix instance. To be a candidate for
256 /*	destruction an instance must be disabled, stopped and its
257 /*	queue must not contain any messages. Attempts to destroy
258 /*	the primary Postfix instance trigger a fatal error, without
259 /*	destroying the instance.
260 /* .sp
261 /*	The instance is removed from the primary instance main.cf
262 /*	file's alternate_config_directories parameter and its data,
263 /*	queue and configuration directories are cleaned of files
264 /*	and directories created by the Postfix system. The main.cf
265 /*	and master.cf files are removed from the configuration
266 /*	directory even if they have been modified since initial
267 /*	creation. Finally, the instance is "deported" from the list
268 /*	of managed instances.
269 /* .sp
270 /*	If other files are present in instance private directories,
271 /*	the directories may not be fully removed, a warning is
272 /*	logged to alert the administrator. It is expected that an
273 /*	instance built using "fresh" directories via the \fBcreate\fR
274 /*	action will be fully removed by the \fBdestroy\fR action
275 /*	(if first disabled). If the instance configuration and queue
276 /*	directories are populated with additional files	(access and
277 /*	rewriting tables, chroot jail content, etc.) the instance
278 /*	directories will not be fully removed.
279 /* .sp
280 /*	The \fBdestroy\fR action triggers potentially dangerous
281 /*	file removal operations. Make sure the instance's data,
282 /*	queue and configuration directories are set correctly and
283 /*	do not contain any valuable files.
284 /* .IP \fBdeport\fR
285 /*	Deport a secondary instance from the list of managed
286 /*	instances. This deletes the instance configuration directory
287 /*	from the primary instance's multi_instance_directories list,
288 /*	but does not remove any files or directories.
289 /* .IP \fBassign\fR
290 /*	Assign a new instance name or a new group name to the
291 /*	selected instance.  Use "\fB-G -\fR" to specify "no group"
292 /*	and "\fB-I -\fR" to specify "no name".  If you choose to
293 /*	make an instance "nameless", set a suitable syslog_name in
294 /*	the corresponding main.cf file.
295 /* .IP \fBenable\fR
296 /*	Mark the selected instance as enabled. This just sets the
297 /*	multi_instance_enable parameter to "yes" in the instance's
298 /*	main.cf file.
299 /* .IP \fBdisable\fR
300 /*	Mark the selected instance as disabled. This means that
301 /*	the instance will not be started etc. with "postfix start",
302 /*	"postmulti -p start" and so on. The instance can still be
303 /*	started etc. with "postfix -c config-directory start".
304 /* .SH "Other options"
305 /* .IP \fB-v\fR
306 /*	Enable verbose logging for debugging purposes. Multiple
307 /*	\fB-v\fR options make the software increasingly verbose.
308 /* .RE
309 /* ENVIRONMENT
310 /* .ad
311 /* .fi
312 /*	The \fBpostmulti\fR(1) command exports the following environment
313 /*	variables before executing the requested \fIcommand\fR for a given
314 /*	instance:
315 /* .IP \fBMAIL_VERBOSE\fR
316 /*	This is set when the -v command-line option is present.
317 /* .IP \fBMAIL_CONFIG\fR
318 /*	The location of the configuration directory of the instance.
319 /* CONFIGURATION PARAMETERS
320 /* .ad
321 /* .fi
322 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
323 /*	The default location of the Postfix main.cf and master.cf
324 /*	configuration files.
325 /* .IP "\fBdaemon_directory (see 'postconf -d' output)\fR"
326 /*	The directory with Postfix support programs and daemon programs.
327 /* .IP "\fBimport_environment (see 'postconf -d' output)\fR"
328 /*	The list of environment parameters that a Postfix process will
329 /*	import from a non-Postfix parent process.
330 /* .IP "\fBmulti_instance_directories (empty)\fR"
331 /*	An optional list of non-default Postfix configuration directories;
332 /*	these directories belong to additional Postfix instances that share
333 /*	the Postfix executable files and documentation with the default
334 /*	Postfix instance, and that are started, stopped, etc., together
335 /*	with the default Postfix instance.
336 /* .IP "\fBmulti_instance_group (empty)\fR"
337 /*	The optional instance group name of this Postfix instance.
338 /* .IP "\fBmulti_instance_name (empty)\fR"
339 /*	The optional instance name of this Postfix instance.
340 /* .IP "\fBmulti_instance_enable (no)\fR"
341 /*	Allow this Postfix instance to be started, stopped, etc., by a
342 /*	multi-instance manager.
343 /* .IP "\fBpostmulti_start_commands (start)\fR"
344 /*	The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager treats
345 /*	as "start" commands.
346 /* .IP "\fBpostmulti_stop_commands (see 'postconf -d' output)\fR"
347 /*	The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager treats
348 /*	as "stop" commands.
349 /* .IP "\fBpostmulti_control_commands (reload flush)\fR"
350 /*	The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager
351 /*	treats as "control" commands, that operate on running instances.
352 /* .IP "\fBsyslog_facility (mail)\fR"
353 /*	The syslog facility of Postfix logging.
354 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
355 /*	The mail system name that is prepended to the process name in syslog
356 /*	records, so that "smtpd" becomes, for example, "postfix/smtpd".
357 /* FILES
358 /*	$daemon_directory/main.cf, stock configuration file
359 /*	$daemon_directory/master.cf, stock configuration file
360 /*	$daemon_directory/postmulti-script, life-cycle helper program
361 /* SEE ALSO
362 /*	postfix(1), Postfix control program
363 /*	postfix-wrapper(5), Postfix multi-instance API
364 /* README FILES
365 /* .ad
366 /* .fi
367 /*	Use "\fBpostconf readme_directory\fR" or "\fBpostconf
368 /*	html_directory\fR" to locate this information.
369 /* .nf
370 /* .na
371 /*	MULTI_INSTANCE_README, Postfix multi-instance management
372 /* HISTORY
373 /* .ad
374 /* .fi
375 /*	The \fBpostmulti\fR(1) command was introduced with Postfix
376 /*	version 2.6.
377 /* LICENSE
378 /* .ad
379 /* .fi
380 /*	The Secure Mailer license must be distributed with this software.
381 /* AUTHOR(S)
382 /*	Victor Duchovni
383 /*	Morgan Stanley
384 /*
385 /*	Wietse Venema
386 /*	IBM T.J. Watson Research
387 /*	P.O. Box 704
388 /*	Yorktown Heights, NY 10598, USA
389 /*--*/
390 
391 /* System library. */
392 
393 #include <sys_defs.h>
394 #include <sys/stat.h>
395 #include <sys/wait.h>
396 #include <vstream.h>
397 #include <stdlib.h>
398 #include <unistd.h>
399 #include <string.h>
400 #include <fcntl.h>
401 #include <syslog.h>
402 #include <errno.h>
403 #include <ctype.h>
404 #ifdef USE_PATHS_H
405 #include <paths.h>
406 #endif
407 #include <stddef.h>
408 
409 /* Utility library. */
410 
411 #include <msg.h>
412 #include <msg_vstream.h>
413 #include <msg_syslog.h>
414 #include <vstream.h>
415 #include <vstring_vstream.h>
416 #include <stringops.h>
417 #include <clean_env.h>
418 #include <argv.h>
419 #include <safe.h>
420 #include <mymalloc.h>
421 #include <htable.h>
422 #include <name_code.h>
423 #include <ring.h>
424 #include <warn_stat.h>
425 
426 /* Global library. */
427 
428 #include <mail_version.h>
429 #include <mail_params.h>
430 #include <mail_conf.h>
431 
432 /* Application-specific. */
433 
434  /*
435   * Configuration parameters, specific to postmulti(1).
436   */
437 char   *var_multi_start_cmds;
438 char   *var_multi_stop_cmds;
439 char   *var_multi_cntrl_cmds;
440 
441  /*
442   * Shared directory pathnames.
443   */
444 typedef struct {
445     const char *param_name;
446     char  **param_value;
447 } SHARED_PATH;
448 
449 static SHARED_PATH shared_dir_table[] = {
450     VAR_COMMAND_DIR, &var_command_dir,
451     VAR_DAEMON_DIR, &var_daemon_dir,
452     0,
453 };
454 
455  /*
456   * Actions.
457   */
458 #define ITER_CMD_POSTFIX	(1<<0)	/* postfix(1) iterator mode */
459 #define ITER_CMD_LIST		(1<<1)	/* listing iterator mode */
460 #define ITER_CMD_GENERIC	(1<<2)	/* generic command iterator mode */
461 
462 #define ITER_CMD_MASK_ALL \
463     (ITER_CMD_POSTFIX | ITER_CMD_LIST | ITER_CMD_GENERIC)
464 
465 #define EDIT_CMD_CREATE		(1<<4)	/* create new instance */
466 #define EDIT_CMD_IMPORT		(1<<5)	/* import existing instance */
467 #define EDIT_CMD_DESTROY	(1<<6)	/* destroy instance */
468 #define EDIT_CMD_DEPORT		(1<<7)	/* export instance */
469 #define EDIT_CMD_ENABLE		(1<<8)	/* enable start/stop */
470 #define EDIT_CMD_DISABLE	(1<<9)	/* disable start/stop */
471 #define EDIT_CMD_ASSIGN		(1<<10)	/* assign name/group */
472 #define EDIT_CMD_INIT		(1<<11)	/* hook into main.cf */
473 
474 #define EDIT_CMD_MASK_ADD	(EDIT_CMD_CREATE | EDIT_CMD_IMPORT)
475 #define EDIT_CMD_MASK_DEL	(EDIT_CMD_DESTROY | EDIT_CMD_DEPORT)
476 #define EDIT_CMD_MASK_ASSIGN	(EDIT_CMD_MASK_ADD | EDIT_CMD_ASSIGN)
477 #define EDIT_CMD_MASK_ENB	(EDIT_CMD_ENABLE | EDIT_CMD_DISABLE)
478 #define EDIT_CMD_MASK_ALL \
479     (EDIT_CMD_MASK_ASSIGN | EDIT_CMD_MASK_DEL | EDIT_CMD_MASK_ENB | \
480 	EDIT_CMD_INIT)
481 
482  /*
483   * Edit command to number mapping, and vice versa.
484   */
485 static NAME_CODE edit_command_table[] = {
486     "create", EDIT_CMD_CREATE,
487     "import", EDIT_CMD_IMPORT,
488     "destroy", EDIT_CMD_DESTROY,
489     "deport", EDIT_CMD_DEPORT,
490     "enable", EDIT_CMD_ENABLE,
491     "disable", EDIT_CMD_DISABLE,
492     "assign", EDIT_CMD_ASSIGN,
493     "init", EDIT_CMD_INIT,
494     0, -1,
495 };
496 
497 #define EDIT_CMD_CODE(str) \
498 	name_code(edit_command_table, NAME_CODE_FLAG_STRICT_CASE, (str))
499 #define EDIT_CMD_STR(code)	str_name_code(edit_command_table, (code))
500 
501  /*
502   * Mandatory prefix for non-empty instance names.
503   */
504 #ifndef NAME_PREFIX
505 #define NAME_PREFIX "postfix-"
506 #endif
507 #define HAS_NAME_PREFIX(name) \
508      (strncmp((name), NAME_PREFIX, sizeof(NAME_PREFIX)-1) == 0)
509 #define NEED_NAME_PREFIX(name) \
510     ((name) != 0 && strcmp((name), "-") != 0 && !HAS_NAME_PREFIX(name))
511 #define NAME_SUFFIX(name) ((name) + sizeof(NAME_PREFIX) - 1)
512 
513  /*
514   * In-core instance structure. Only private information is kept here.
515   */
516 typedef struct instance {
517     RING    ring;			/* linkage. */
518     char   *config_dir;			/* private */
519     char   *queue_dir;			/* private */
520     char   *data_dir;			/* private */
521     char   *name;			/* null or name */
522     char   *gname;			/* null or group */
523     int     enabled;			/* start/stop enable */
524     int     primary;			/* special */
525 } INSTANCE;
526 
527  /*
528   * Managed instance list (edit mode and iterator mode).
529   */
530 static RING instance_hd[1];		/* instance list head */
531 
532 #define RING_TO_INSTANCE(ring_ptr)	RING_TO_APPL(ring_ptr, INSTANCE, ring)
533 #define RING_PTR_OF(x)			(&((x)->ring))
534 
535 #define FOREACH_INSTANCE(entry) \
536     for ((entry) = instance_hd; \
537 	 ((entry) = ring_succ(entry)) != instance_hd;)
538 
539 #define FOREACH_SECONDARY_INSTANCE(entry) \
540     for ((entry) = ring_succ(instance_hd); \
541 	 ((entry) = ring_succ(entry)) != instance_hd;)
542 
543 #define NEXT_ITERATOR_INSTANCE(flags, entry) \
544     (((flags) & ITER_FLAG_REVERSE) ? ring_pred(entry) : ring_succ(entry))
545 
546 #define FOREACH_ITERATOR_INSTANCE(flags, entry) \
547     for ((entry) = instance_hd; \
548 	((entry) = NEXT_ITERATOR_INSTANCE(flags, (entry))) != instance_hd;)
549 
550  /*
551   * Instance selection. One can either select all instances, select by
552   * instance name, or select by instance group.
553   */
554 typedef struct {
555     int     type;			/* see below */
556     char   *name;			/* undefined or name */
557 } INST_SELECTION;
558 
559 #define INST_SEL_NONE		0	/* default: no selection */
560 #define INST_SEL_ALL		1	/* select all instances */
561 #define INST_SEL_NAME		2	/* select instance name */
562 #define INST_SEL_GROUP		3	/* select instance group */
563 
564  /*
565   * Instance name assignment. Each instance may be assigned an instance name
566   * (this must be globally unique within a multi-instance cluster) or an
567   * instance group name (this is intended to be shared). Externally, empty
568   * names may be represented as "-". Internally, we use "" only, to simplify
569   * the code.
570   */
571 typedef struct {
572     char   *name;			/* null or assigned instance name */
573     char   *gname;			/* null or assigned group name */
574 } NAME_ASSIGNMENT;
575 
576  /*
577   * Iterator controls for non-edit commands. One can reverse the iteration
578   * order, or give special treatment to disabled instances.
579   */
580 #define ITER_FLAG_DEFAULT	0	/* default setting */
581 #define ITER_FLAG_REVERSE	(1<<0)	/* reverse iteration order */
582 #define ITER_FLAG_CHECK_DISABLED (1<<1)	/* check disabled instances */
583 #define ITER_FLAG_SKIP_DISABLED	(1<<2)	/* skip disabled instances */
584 
585  /*
586   * Environment export controls for edit commands. postmulti(1) exports only
587   * things that need to be updated.
588   */
589 #define EXP_FLAG_MULTI_DIRS	(1<<0)	/* export multi_instance_directories */
590 #define EXP_FLAG_MULTI_NAME	(1<<1)	/* export multi_instance_name */
591 #define EXP_FLAG_MULTI_GROUP	(1<<2)	/* export multi_instance_group */
592 
593  /*
594   * To detect conflicts, each instance name and each shared or private
595   * pathname is registered in one place, with its owner. Everyone must
596   * register their claims when they join, and will be rejected in case of
597   * conlict.
598   *
599   * Each claim value involves a parameter value (either a directory name or an
600   * instance name). Each claim owner is the config_directory pathname plus
601   * the parameter name.
602   *
603   * XXX: No multi.cf lock file, so this is not race-free.
604   */
605 static HTABLE *claim_table;
606 
607 #define IS_CLAIMED_BY(name) \
608     (claim_table ? htable_find(claim_table, (name)) : 0)
609 
610  /*
611   * Forward references.
612   */
613 static int iterate_command(int, int, char **, INST_SELECTION *);
614 static int match_instance_selection(INSTANCE *, INST_SELECTION *);
615 
616  /*
617   * Convenience.
618   */
619 #define INSTANCE_NAME(i) ((i)->name ? (i)->name : (i)->config_dir)
620 #define STR(buf)	vstring_str(buf)
621 
622 /* register_claim - register claim or bust */
623 
624 static void register_claim(const char *instance_path, const char *param_name,
625 			           const char *param_value)
626 {
627     const char *myname = "register_claim";
628     char   *requestor;
629     const char *owner;
630 
631     /*
632      * Sanity checks.
633      */
634     if (instance_path == 0 || *instance_path == 0)
635 	msg_panic("%s: no or empty instance pathname", myname);
636     if (param_name == 0 || *param_name == 0)
637 	msg_panic("%s: no or empty parameter name", myname);
638     if (param_value == 0)
639 	msg_panic("%s: no parameter value", myname);
640 
641     /*
642      * Make a claim or report a conflict.
643      */
644     if (claim_table == 0)
645 	claim_table = htable_create(100);
646     requestor = concatenate(instance_path, ", ", param_name, (char *) 0);
647     if ((owner = htable_find(claim_table, param_value)) == 0) {
648 	(void) htable_enter(claim_table, param_value, requestor);
649     } else if (strcmp(owner, requestor) == 0) {
650 	myfree(requestor);
651     } else {
652 	msg_fatal("instance %s, %s=%s conflicts with instance %s=%s",
653 		instance_path, param_name, param_value, owner, param_value);
654     }
655 }
656 
657 /* claim_instance_attributes - claim multiple private instance attributes */
658 
659 static void claim_instance_attributes(INSTANCE *ip)
660 {
661 
662     /*
663      * Detect instance name or pathname conflicts between this instance and
664      * other instances. XXX: No multi.cf lock file, so this is not race-free.
665      */
666     if (ip->name)
667 	register_claim(ip->config_dir, VAR_MULTI_NAME, ip->name);
668     register_claim(ip->config_dir, VAR_CONFIG_DIR, ip->config_dir);
669     register_claim(ip->config_dir, VAR_QUEUE_DIR, ip->queue_dir);
670     register_claim(ip->config_dir, VAR_DATA_DIR, ip->data_dir);
671 }
672 
673 /* alloc_instance - allocate a single instance object */
674 
675 static INSTANCE *alloc_instance(const char *config_dir)
676 {
677     INSTANCE *ip = (INSTANCE *) mymalloc(sizeof(INSTANCE));
678 
679     ring_init(RING_PTR_OF(ip));
680     ip->config_dir = config_dir ? mystrdup(config_dir) : 0;
681     ip->queue_dir = 0;
682     ip->data_dir = 0;
683     ip->name = 0;
684     ip->gname = 0;
685     ip->enabled = 0;
686     ip->primary = 0;
687 
688     return (ip);
689 }
690 
691 #if 0
692 
693 /* free_instance - free a single instance object */
694 
695 static void free_instance(INSTANCE *ip)
696 {
697 
698     /*
699      * If we continue after secondary main.cf file read error, we must be
700      * prepared for the case that some parameters may be missing.
701      */
702     if (ip->name)
703 	myfree(ip->name);
704     if (ip->gname)
705 	myfree(ip->gname);
706     if (ip->config_dir)
707 	myfree(ip->config_dir);
708     if (ip->queue_dir)
709 	myfree(ip->queue_dir);
710     if (ip->data_dir)
711 	myfree(ip->data_dir);
712     myfree((char *) ip);
713 }
714 
715 #endif
716 
717 /* insert_instance - insert instance before selected location, claim names */
718 
719 static void insert_instance(INSTANCE *ip, INST_SELECTION *selection)
720 {
721     RING   *old;
722 
723 #define append_instance(ip) insert_instance((ip), (INST_SELECTION *) 0)
724 
725     /*
726      * Insert instance before the selected site.
727      */
728     claim_instance_attributes(ip);
729     if (ring_succ(instance_hd) == 0)
730 	ring_init(instance_hd);
731     if (selection && selection->type != INST_SEL_NONE) {
732 	FOREACH_SECONDARY_INSTANCE(old) {
733 	    if (match_instance_selection(RING_TO_INSTANCE(old), selection)) {
734 		ring_prepend(old, RING_PTR_OF(ip));
735 		return;
736 	    }
737 	}
738 	if (selection->type != INST_SEL_ALL)
739 	    msg_fatal("No matching secondary instances");
740     }
741     ring_prepend(instance_hd, RING_PTR_OF(ip));
742 }
743 
744 /* create_primary_instance - synthetic entry for primary instance */
745 
746 static INSTANCE *create_primary_instance(void)
747 {
748     INSTANCE *ip = alloc_instance(var_config_dir);
749 
750     /*
751      * There is no need to load primary instance paramater settings from
752      * file. We already have the main.cf parameters of interest in memory.
753      */
754 #define SAVE_INSTANCE_NAME(val) (*(val) ? mystrdup(val) : 0)
755 
756     ip->name = SAVE_INSTANCE_NAME(var_multi_name);
757     ip->gname = SAVE_INSTANCE_NAME(var_multi_group);
758     ip->enabled = var_multi_enable;
759     ip->queue_dir = mystrdup(var_queue_dir);
760     ip->data_dir = mystrdup(var_data_dir);
761     ip->primary = 1;
762     return (ip);
763 }
764 
765 /* load_instance - read instance parameters from config_dir/main.cf */
766 
767 static INSTANCE *load_instance(INSTANCE *ip)
768 {
769     VSTREAM *pipe;
770     VSTRING *buf;
771     char   *name;
772     char   *value;
773     ARGV   *cmd;
774     int     count = 0;
775     static NAME_CODE bool_code[] = {
776 	CONFIG_BOOL_YES, 1,
777 	CONFIG_BOOL_NO, 0,
778 	0, -1,
779     };
780 
781     /*
782      * XXX: We could really use a "postconf -E" to expand values in the
783      * context of the target main.cf!
784      */
785 #define REQUEST_PARAM_COUNT 5			/* # of requested parameters */
786 
787     cmd = argv_alloc(REQUEST_PARAM_COUNT + 3);
788     name = concatenate(var_command_dir, "/", "postconf", (char *) 0);
789     argv_add(cmd, name, "-c", ip->config_dir,
790 	     VAR_QUEUE_DIR, VAR_DATA_DIR,
791 	     VAR_MULTI_NAME, VAR_MULTI_GROUP, VAR_MULTI_ENABLE,
792 	     (char *) 0);
793     myfree(name);
794     pipe = vstream_popen(O_RDONLY, VSTREAM_POPEN_ARGV, cmd->argv,
795 			 VSTREAM_POPEN_END);
796     argv_free(cmd);
797     if (pipe == 0)
798 	msg_fatal("Cannot parse %s/main.cf file: %m", ip->config_dir);
799 
800     /*
801      * Read parameter settings from postconf. See also comments below on
802      * whether we should continue or skip groups after error instead of
803      * bailing out immediately.
804      */
805     buf = vstring_alloc(100);
806     while (vstring_get_nonl(buf, pipe) != VSTREAM_EOF) {
807 	if (split_nameval(STR(buf), &name, &value))
808 	    msg_fatal("Invalid %s/main.cf parameter: %s",
809 		      ip->config_dir, STR(buf));
810 	if (strcmp(name, VAR_QUEUE_DIR) == 0 && ++count)
811 	    ip->queue_dir = mystrdup(value);
812 	else if (strcmp(name, VAR_DATA_DIR) == 0 && ++count)
813 	    ip->data_dir = mystrdup(value);
814 	else if (strcmp(name, VAR_MULTI_NAME) == 0 && ++count)
815 	    ip->name = SAVE_INSTANCE_NAME(value);
816 	else if (strcmp(name, VAR_MULTI_GROUP) == 0 && ++count)
817 	    ip->gname = SAVE_INSTANCE_NAME(value);
818 	else if (strcmp(name, VAR_MULTI_ENABLE) == 0 && ++count) {
819 	    /* mail_conf_bool(3) is case insensitive! */
820 	    ip->enabled = name_code(bool_code, NAME_CODE_FLAG_NONE, value);
821 	    if (ip->enabled < 0)
822 		msg_fatal("Unexpected %s/main.cf entry: %s = %s",
823 			  ip->config_dir, VAR_MULTI_ENABLE, value);
824 	}
825     }
826     vstring_free(buf);
827 
828     /*
829      * XXX We should not bail out while reading a bad secondary main.cf file.
830      * When we manage dozens or more instances, the likelihood increases that
831      * some file will be damaged or missing after a system crash. That is not
832      * a good reason to prevent undamaged Postfix instances from starting.
833      */
834     if (count != REQUEST_PARAM_COUNT)
835 	msg_fatal("Failed to obtain all required %s/main.cf parameters",
836 		  ip->config_dir);
837 
838     if (vstream_pclose(pipe))
839 	msg_fatal("Cannot parse %s/main.cf file", ip->config_dir);
840     return (ip);
841 }
842 
843 /* load_all_instances - compute list of Postfix instances */
844 
845 static void load_all_instances(void)
846 {
847     INSTANCE *primary_instance;
848     char  **cpp;
849     ARGV   *secondary_names;
850 
851     /*
852      * Avoid unexpected behavior when $multi_instance_directories contains
853      * only comma characters. Count the actual number of elements, before we
854      * decide that the list is empty.
855      */
856     secondary_names = argv_split(var_multi_conf_dirs, "\t\n\r, ");
857 
858     /*
859      * First, the primary instance.  This is synthesized out of thin air.
860      */
861     primary_instance = create_primary_instance();
862     if (secondary_names->argc == 0)
863 	primary_instance->enabled = 1;		/* Single-instance mode */
864     append_instance(primary_instance);
865 
866     /*
867      * Next, instances defined in $multi_instance_directories. Note:
868      * load_instance() has side effects on the global config dictionary, but
869      * this does not affect the values that have already been extracted into
870      * C variables.
871      */
872     for (cpp = secondary_names->argv; *cpp != 0; cpp++)
873 	append_instance(load_instance(alloc_instance(*cpp)));
874 
875     argv_free(secondary_names);
876 }
877 
878 /* match_instance_selection - match all/name/group constraints */
879 
880 static int match_instance_selection(INSTANCE *ip, INST_SELECTION *selection)
881 {
882     char   *iname;
883     char   *name;
884 
885     /*
886      * When selecting (rather than assigning names) an instance, we match by
887      * the instance name, config_directory path, or the instance name suffix
888      * (name without mandatory prefix). Selecting "-" selects the primary
889      * instance.
890      */
891     switch (selection->type) {
892     case INST_SEL_NONE:
893 	return (0);
894     case INST_SEL_ALL:
895 	return (1);
896     case INST_SEL_GROUP:
897 	return (ip->gname != 0 && strcmp(selection->name, ip->gname) == 0);
898     case INST_SEL_NAME:
899 	name = selection->name;
900 	if (*name == '/' || ip->name == 0)
901 	    iname = ip->config_dir;
902 	else if (!HAS_NAME_PREFIX(name) && HAS_NAME_PREFIX(ip->name))
903 	    iname = NAME_SUFFIX(ip->name);
904 	else
905 	    iname = ip->name;
906 	return (strcmp(name, iname) == 0
907 		|| (ip->primary && strcmp(name, "-") == 0));
908     default:
909 	msg_panic("match_instance_selection: unknown selection type: %d",
910 		  selection->type);
911     }
912 }
913 
914 /* check_setenv - setenv() with extreme prejudice */
915 
916 static void check_setenv(const char *name, const char *value)
917 {
918 #define CLOBBER 1
919     if (setenv(name, value, CLOBBER) < 0)
920 	msg_fatal("setenv: %m");
921 }
922 
923 /* prepend_command_path - prepend command_directory to PATH */
924 
925 static void prepend_command_path(void)
926 {
927     char   *cmd_path;
928 
929     /*
930      * Carefully prepend "$command_directory:" to PATH. We can free the
931      * buffer after check_setenv(), since the value is copied there.
932      */
933     cmd_path = safe_getenv("PATH");
934     cmd_path = concatenate(var_command_dir, ":", (cmd_path && *cmd_path) ?
935 			   cmd_path : ROOT_PATH, (char *) 0);
936     check_setenv("PATH", cmd_path);
937     myfree(cmd_path);
938 }
939 
940 /* check_shared_dir_status - check and claim shared directories */
941 
942 static void check_shared_dir_status(void)
943 {
944     struct stat st;
945     const SHARED_PATH *sp;
946 
947     for (sp = shared_dir_table; sp->param_name; ++sp) {
948 	if (stat(sp->param_value[0], &st) < 0)
949 	    msg_fatal("%s = '%s': directory not found: %m",
950 		      sp->param_name, sp->param_value[0]);
951 	if (!S_ISDIR(st.st_mode))
952 	    msg_fatal("%s = '%s' is not a directory",
953 		      sp->param_name, sp->param_value[0]);
954 	register_claim(var_config_dir, sp->param_name, sp->param_value[0]);
955     }
956 }
957 
958 /* check_safe_name - allow instance or group name with only "safe" characters */
959 
960 static int check_safe_name(const char *s)
961 {
962 #define SAFE_PUNCT	"!@%-_=+:./"
963     if (*s == 0)
964 	return (0);
965     for (; *s; ++s) {
966 	if (!ISALNUM(*s) && !strchr(SAFE_PUNCT, *s))
967 	    return (0);
968     }
969     return (1);
970 }
971 
972 /* check_name_assignments - Check validity of assigned instance or group name */
973 
974 static void check_name_assignments(NAME_ASSIGNMENT *assignment)
975 {
976 
977     /*
978      * Syntax check the assigned instance name. This name is also used to
979      * generate directory pathnames, so we must not allow "/" characters.
980      *
981      * The value "" will clear the name and is always valid. The command-line
982      * parser has already converted "-" into "", to simplify implementation.
983      */
984     if (assignment->name && *assignment->name) {
985 	if (!check_safe_name(assignment->name))
986 	    msg_fatal("Unsafe characters in new instance name: '%s'",
987 		      assignment->name);
988 	if (strchr(assignment->name, '/'))
989 	    msg_fatal("Illegal '/' character in new instance name: '%s'",
990 		      assignment->name);
991 	if (NEED_NAME_PREFIX(assignment->name))
992 	    msg_fatal("New instance name must start with '%s'",
993 		      NAME_PREFIX);
994     }
995 
996     /*
997      * Syntax check the assigned group name.
998      */
999     if (assignment->gname && *assignment->gname) {
1000 	if (!check_safe_name(assignment->gname))
1001 	    msg_fatal("Unsafe characters in '-G %s'", assignment->gname);
1002     }
1003 }
1004 
1005 /* do_name_assignments - assign instance/group names */
1006 
1007 static int do_name_assignments(INSTANCE *target, NAME_ASSIGNMENT *assignment)
1008 {
1009     int     export_flags = 0;
1010 
1011     /*
1012      * The command-line parser has already converted "-" into "", to simplify
1013      * implementation.
1014      */
1015     if (assignment->name
1016 	&& strcmp(assignment->name, target->name ? target->name : "")) {
1017 	register_claim(target->config_dir, VAR_MULTI_NAME, assignment->name);
1018 	if (target->name)
1019 	    myfree(target->name);
1020 	target->name = SAVE_INSTANCE_NAME(assignment->name);
1021 	export_flags |= EXP_FLAG_MULTI_NAME;
1022     }
1023     if (assignment->gname
1024 	&& strcmp(assignment->gname, target->gname ? target->gname : "")) {
1025 	if (target->gname)
1026 	    myfree(target->gname);
1027 	target->gname = SAVE_INSTANCE_NAME(assignment->gname);
1028 	export_flags |= EXP_FLAG_MULTI_GROUP;
1029     }
1030     return (export_flags);
1031 }
1032 
1033 /* make_private_path - generate secondary pathname using primary as template */
1034 
1035 static char *make_private_path(const char *param_name,
1036 			               const char *primary_value,
1037 			               NAME_ASSIGNMENT *assignment)
1038 {
1039     char   *path;
1040     char   *base;
1041     char   *end;
1042 
1043     /*
1044      * The command-line parser has already converted "-" into "", to simplify
1045      * implementation.
1046      */
1047     if (assignment->name == 0 || *assignment->name == 0)
1048 	msg_fatal("Missing %s parameter value", param_name);
1049 
1050     if (*primary_value != '/')
1051 	msg_fatal("Invalid default %s parameter value: '%s': "
1052 		  "specify an absolute pathname",
1053 		  param_name, primary_value);
1054 
1055     base = mystrdup(primary_value);
1056     if ((end = strrchr(base, '/')) != 0) {
1057 	/* Drop trailing slashes */
1058 	if (end[1] == '\0') {
1059 	    while (--end > base && *end == '/')
1060 		*end = '\0';
1061 	    end = strrchr(base, '/');
1062 	}
1063 	/* Drop last path component */
1064 	while (end > base && *end == '/')
1065 	    *end-- = '\0';
1066     }
1067     path = concatenate(base[1] ? base : "", "/",
1068 		       assignment->name, (char *) 0);
1069     myfree(base);
1070     return (path);
1071 }
1072 
1073 /* assign_new_parameter - assign new instance private name=value */
1074 
1075 static void assign_new_parameter(INSTANCE *new, int edit_cmd,
1076 				         const char *arg)
1077 {
1078     char   *saved_arg;
1079     char   *name;
1080     char   *value;
1081     char   *end;
1082     char  **target = 0;
1083 
1084     /*
1085      * With "import", only config_directory is specified on the command line
1086      * (either explicitly as config_directory=/path/name, or implicitly as
1087      * instance name). The other private directory pathnames are taken from
1088      * the existing instance's main.cf file.
1089      *
1090      * With "create", all private pathname parameters are specified on the
1091      * command line, or generated from an instance name.
1092      */
1093     saved_arg = mystrdup(arg);
1094     if (split_nameval(saved_arg, &name, &value))
1095 	msg_fatal("Malformed parameter setting '%s'", arg);
1096 
1097     if (strcmp(VAR_CONFIG_DIR, name) == 0) {
1098 	target = &new->config_dir;
1099     } else if (edit_cmd != EDIT_CMD_IMPORT) {
1100 	if (strcmp(VAR_QUEUE_DIR, name) == 0) {
1101 	    target = &new->queue_dir;
1102 	} else if (strcmp(VAR_DATA_DIR, name) == 0) {
1103 	    target = &new->data_dir;
1104 	}
1105     }
1106     if (target == 0)
1107 	msg_fatal("Parameter '%s' not valid with action %s",
1108 		  name, EDIT_CMD_STR(edit_cmd));
1109 
1110     /*
1111      * Extract and assign the parameter value. We do a limited number of
1112      * checks here. Conflicts between instances are checked by the caller.
1113      * More checks may be implemented in the helper script if inspired.
1114      */
1115     if (*value != '/')
1116 	msg_fatal("Parameter setting '%s' is not an absolute path", name);
1117 
1118     /* Tolerate+trim trailing "/" from readline completion */
1119     for (end = value + strlen(value) - 1; end > value && *end == '/'; --end)
1120 	*end = 0;
1121 
1122     /* No checks here for "/." or other shoot-foot silliness. */
1123     if (end == value)
1124 	msg_fatal("Parameter setting '%s' is the root directory", name);
1125 
1126     if (*target)
1127 	myfree(*target);
1128     *target = mystrdup(value);
1129 
1130     /*
1131      * Cleanup.
1132      */
1133     myfree(saved_arg);
1134 }
1135 
1136 /* assign_new_parameters - initialize new instance private parameters */
1137 
1138 static void assign_new_parameters(INSTANCE *new, int edit_cmd,
1139 			           char **argv, NAME_ASSIGNMENT *assignment)
1140 {
1141     const char *owner;
1142 
1143     /*
1144      * Sanity check the explicit parameter settings. More stringent checks
1145      * may take place in the helper script.
1146      */
1147     while (*argv)
1148 	assign_new_parameter(new, edit_cmd, *argv++);
1149 
1150     /*
1151      * Initialize any missing private directory pathnames, using the primary
1152      * configuration directory parameter values as a template, and using the
1153      * assigned instance name to fill in the blanks.
1154      *
1155      * When importing an existing instance, load private directory pathnames
1156      * from its main.cf file.
1157      */
1158     if (new->config_dir == 0)
1159 	new->config_dir =
1160 	    make_private_path(VAR_CONFIG_DIR, var_config_dir, assignment);
1161     /* Needed for better-quality error message. */
1162     if ((owner = IS_CLAIMED_BY(new->config_dir)) != 0)
1163 	msg_fatal("new %s=%s is already in use by instance %s=%s",
1164 		  VAR_CONFIG_DIR, new->config_dir, owner, new->config_dir);
1165     if (edit_cmd != EDIT_CMD_IMPORT) {
1166 	if (new->queue_dir == 0)
1167 	    new->queue_dir =
1168 		make_private_path(VAR_QUEUE_DIR, var_queue_dir, assignment);
1169 	if (new->data_dir == 0)
1170 	    new->data_dir =
1171 		make_private_path(VAR_DATA_DIR, var_data_dir, assignment);
1172     } else {
1173 	load_instance(new);
1174     }
1175 }
1176 
1177 /* export_helper_environment - update environment settings for helper command */
1178 
1179 static void export_helper_environment(INSTANCE *target, int export_flags)
1180 {
1181     ARGV   *import_env;
1182     VSTRING *multi_dirs;
1183     const SHARED_PATH *sp;
1184     RING   *entry;
1185 
1186     /*
1187      * Environment import filter, to enforce consistent behavior whether this
1188      * command is started by hand, or at system boot time. This is necessary
1189      * because some shell scripts use environment settings to override
1190      * main.cf settings.
1191      */
1192     import_env = argv_split(var_import_environ, ", \t\r\n");
1193     clean_env(import_env->argv);
1194     argv_free(import_env);
1195 
1196     /*
1197      * Prepend $command_directory: to PATH. This supposedly ensures that
1198      * naive programs will execute commands from the right Postfix version.
1199      */
1200     prepend_command_path();
1201 
1202     /*
1203      * The following ensures that Postfix's own programs will target the
1204      * primary instance.
1205      */
1206     check_setenv(CONF_ENV_PATH, var_config_dir);
1207 
1208     /*
1209      * Export the parameter settings that are shared between instances.
1210      */
1211     for (sp = shared_dir_table; sp->param_name; ++sp)
1212 	check_setenv(sp->param_name, sp->param_value[0]);
1213 
1214     /*
1215      * Export the target instance's private directory locations.
1216      */
1217     check_setenv(VAR_CONFIG_DIR, target->config_dir);
1218     check_setenv(VAR_QUEUE_DIR, target->queue_dir);
1219     check_setenv(VAR_DATA_DIR, target->data_dir);
1220 
1221     /*
1222      * With operations that add or delete a secondary instance, we export the
1223      * modified multi_instance_directories parameter value for the primary
1224      * Postfix instance.
1225      */
1226     if (export_flags & EXP_FLAG_MULTI_DIRS) {
1227 	multi_dirs = vstring_alloc(100);
1228 	FOREACH_SECONDARY_INSTANCE(entry) {
1229 	    if (VSTRING_LEN(multi_dirs) > 0)
1230 		VSTRING_ADDCH(multi_dirs, ' ');
1231 	    vstring_strcat(multi_dirs, RING_TO_INSTANCE(entry)->config_dir);
1232 	}
1233 	check_setenv(VAR_MULTI_CONF_DIRS, STR(multi_dirs));
1234 	vstring_free(multi_dirs);
1235     }
1236 
1237     /*
1238      * Export updates for the instance name and group. Empty value (or no
1239      * export) means don't update, "-" means clear.
1240      */
1241     if (export_flags & EXP_FLAG_MULTI_NAME)
1242 	check_setenv(VAR_MULTI_NAME, target->name && *target->name ?
1243 		     target->name : "-");
1244 
1245     if (export_flags & EXP_FLAG_MULTI_GROUP)
1246 	check_setenv(VAR_MULTI_GROUP, target->gname && *target->gname ?
1247 		     target->gname : "-");
1248 
1249     /*
1250      * If we would implement enable/disable commands by exporting the updated
1251      * parameter value, then we could skip commands that have no effect, just
1252      * like we can skip "assign" commands that make no change.
1253      */
1254 }
1255 
1256 /* install_new_instance - install and return newly created instance */
1257 
1258 static INSTANCE *install_new_instance(int edit_cmd, char **argv,
1259 				              INST_SELECTION *selection,
1260 				              NAME_ASSIGNMENT *assignment,
1261 				              int *export_flags)
1262 {
1263     INSTANCE *new;
1264 
1265     new = alloc_instance((char *) 0);
1266     check_name_assignments(assignment);
1267     assign_new_parameters(new, edit_cmd, argv, assignment);
1268     *export_flags |=
1269 	(do_name_assignments(new, assignment) | EXP_FLAG_MULTI_DIRS);
1270     insert_instance(new, selection);
1271     return (new);
1272 }
1273 
1274 /* update_instance - update existing instance, return export flags */
1275 
1276 static int update_instance(INSTANCE *target, NAME_ASSIGNMENT *assignment)
1277 {
1278     int     export_flags;
1279 
1280     check_name_assignments(assignment);
1281     export_flags = do_name_assignments(target, assignment);
1282     return (export_flags);
1283 }
1284 
1285 /* select_existing_instance - return instance selected for management */
1286 
1287 static INSTANCE *select_existing_instance(INST_SELECTION *selection,
1288 					          int unlink_flag,
1289 					          int *export_flags)
1290 {
1291     INSTANCE *selected = 0;
1292     RING   *entry;
1293     INSTANCE *ip;
1294 
1295 #define DONT_UNLINK	0
1296 #define DO_UNLINK	1
1297 
1298     if (selection->type != INST_SEL_NAME)
1299 	msg_fatal("Select an instance via '-i name'");
1300 
1301     /* Find the selected instance and its predecessor */
1302     FOREACH_INSTANCE(entry) {
1303 	if (match_instance_selection(ip = RING_TO_INSTANCE(entry), selection)) {
1304 	    selected = ip;
1305 	    break;
1306 	}
1307     }
1308 
1309     if (selected == 0)
1310 	msg_fatal("No instance named %s", selection->name);
1311 
1312     if (unlink_flag) {
1313 	/* Splice the target instance out of the list */
1314 	if (ring_pred(entry) == instance_hd)
1315 	    msg_fatal("Cannot remove the primary instance");
1316 	if (selected->enabled)
1317 	    msg_fatal("Cannot remove enabled instances");
1318 	ring_detach(entry);
1319 	if (export_flags == 0)
1320 	    msg_panic("select_existing_instance: no export flags");
1321 	*export_flags |= EXP_FLAG_MULTI_DIRS;
1322     }
1323     return (selected);
1324 }
1325 
1326 /* manage - create/destroy/... manage instances */
1327 
1328 static NORETURN manage(int edit_cmd, int argc, char **argv,
1329 		               INST_SELECTION *selection,
1330 		               NAME_ASSIGNMENT *assignment)
1331 {
1332     char   *cmd;
1333     INSTANCE *target;
1334     int     export_flags;
1335 
1336     /*
1337      * Edit mode is not subject to iterator controls.
1338      */
1339 #define NO_EXPORT_FLAGS		((int *) 0)
1340     export_flags = 0;
1341 
1342     switch (edit_cmd) {
1343     case EDIT_CMD_INIT:
1344 	target = create_primary_instance();
1345 	break;
1346 
1347     case EDIT_CMD_CREATE:
1348     case EDIT_CMD_IMPORT:
1349 	load_all_instances();
1350 	target = install_new_instance(edit_cmd, argv, selection,
1351 				      assignment, &export_flags);
1352 	break;
1353 
1354     case EDIT_CMD_ASSIGN:
1355 	load_all_instances();
1356 	target =
1357 	    select_existing_instance(selection, DONT_UNLINK, NO_EXPORT_FLAGS);
1358 	export_flags |= update_instance(target, assignment);
1359 	if (export_flags == 0)
1360 	    exit(0);
1361 	break;
1362 
1363     case EDIT_CMD_DESTROY:
1364     case EDIT_CMD_DEPORT:
1365 	load_all_instances();
1366 	target = select_existing_instance(selection, DO_UNLINK, &export_flags);
1367 	break;
1368 
1369     default:
1370 	load_all_instances();
1371 	target =
1372 	    select_existing_instance(selection, DONT_UNLINK, NO_EXPORT_FLAGS);
1373 	break;
1374     }
1375 
1376     /*
1377      * Set up the helper script's process environment, and execute the helper
1378      * script.
1379      */
1380 #define HELPER "postmulti-script"
1381 
1382     export_helper_environment(target, export_flags);
1383     cmd = concatenate(var_daemon_dir, "/" HELPER, (char *) 0);
1384     execl(cmd, cmd, "-e", EDIT_CMD_STR(edit_cmd), (char *) 0);
1385     msg_fatal("%s: %m", cmd);
1386 }
1387 
1388 /* run_user_command - execute external command with requested MAIL_CONFIG env */
1389 
1390 static int run_user_command(INSTANCE *ip, int iter_cmd, int iter_flags,
1391 			            char **argv)
1392 {
1393     WAIT_STATUS_T status;
1394     int     pid;
1395     int     wpid;
1396 
1397     /*
1398      * Set up a process environment. The postfix(1) command needs MAIL_CONFIG
1399      * (or the equivalent command-line option); it overrides everything else.
1400      *
1401      * postmulti(1) typically runs various Postfix utilities (postsuper, ...) in
1402      * the context of one or more instances. It can also run various scripts
1403      * on the users PATH. So we can't clobber the user's PATH, but do want to
1404      * make sure that the utilities in $command_directory are always found in
1405      * the right place (or at all).
1406      */
1407     switch (pid = fork()) {
1408     case -1:
1409 	msg_warn("fork %s: %m", argv[0]);
1410 	return -1;
1411     case 0:
1412 	check_setenv(CONF_ENV_PATH, ip->config_dir);
1413 	if (iter_cmd != ITER_CMD_POSTFIX) {
1414 	    check_setenv(VAR_DAEMON_DIR, var_daemon_dir);
1415 	    check_setenv(VAR_COMMAND_DIR, var_command_dir);
1416 	    check_setenv(VAR_CONFIG_DIR, ip->config_dir);
1417 	    check_setenv(VAR_QUEUE_DIR, ip->queue_dir);
1418 	    check_setenv(VAR_DATA_DIR, ip->data_dir);
1419 	    check_setenv(VAR_MULTI_NAME, ip->name ? ip->name : "");
1420 	    check_setenv(VAR_MULTI_GROUP, ip->gname ? ip->gname : "");
1421 	    check_setenv(VAR_MULTI_ENABLE, ip->enabled ?
1422 			 CONFIG_BOOL_YES : CONFIG_BOOL_NO);
1423 	    prepend_command_path();
1424 	}
1425 
1426 	/*
1427 	 * Replace: postfix -- start ... With: postfix -- check ...
1428 	 */
1429 	if (iter_cmd == ITER_CMD_POSTFIX
1430 	    && (iter_flags & ITER_FLAG_CHECK_DISABLED) && !ip->enabled)
1431 	    argv[2] = "check";
1432 
1433 	execvp(argv[0], argv);
1434 	msg_fatal("execvp %s: %m", argv[0]);
1435     default:
1436 	do {
1437 	    wpid = waitpid(pid, &status, 0);
1438 	} while (wpid == -1 && errno == EINTR);
1439 	return (wpid == -1 ? -1 :
1440 		WIFEXITED(status) ? WEXITSTATUS(status) : 1);
1441     }
1442 }
1443 
1444 /* word_in_list - look up command in start, stop, or control list */
1445 
1446 static int word_in_list(char *cmdlist, const char *cmd)
1447 {
1448     char   *saved;
1449     char   *cp;
1450     char   *elem;
1451 
1452     cp = saved = mystrdup(cmdlist);
1453     while ((elem = mystrtok(&cp, "\t\n\r, ")) != 0 && strcmp(elem, cmd) != 0)
1454 	 /* void */ ;
1455     myfree(saved);
1456     return (elem != 0);
1457 }
1458 
1459 /* iterate_postfix_command - execute postfix(1) command */
1460 
1461 static int iterate_postfix_command(int iter_cmd, int argc, char **argv,
1462 				           INST_SELECTION *selection)
1463 {
1464     int     exit_status;
1465     char   *cmd;
1466     ARGV   *my_argv;
1467     int     iter_flags;
1468 
1469     /*
1470      * Override the iterator controls.
1471      */
1472     if (word_in_list(var_multi_start_cmds, argv[0])) {
1473 	iter_flags = ITER_FLAG_CHECK_DISABLED;
1474     } else if (word_in_list(var_multi_stop_cmds, argv[0])) {
1475 	iter_flags = ITER_FLAG_SKIP_DISABLED | ITER_FLAG_REVERSE;
1476     } else if (word_in_list(var_multi_cntrl_cmds, argv[0])) {
1477 	iter_flags = ITER_FLAG_SKIP_DISABLED;
1478     } else {
1479 	iter_flags = 0;
1480     }
1481 
1482     /*
1483      * Override the command line in a straightforward manner: prepend
1484      * "postfix --" to the command arguments. Other overrides (environment,
1485      * start -> check) are implemented below the iterator.
1486      */
1487 #define POSTFIX_CMD	"postfix"
1488 
1489     my_argv = argv_alloc(argc + 2);
1490     cmd = concatenate(var_command_dir, "/" POSTFIX_CMD, (char *) 0);
1491     argv_add(my_argv, cmd, "--", (char *) 0);
1492     myfree(cmd);
1493     while (*argv)
1494 	argv_add(my_argv, *argv++, (char *) 0);
1495 
1496     /*
1497      * Execute the command for all applicable Postfix instances.
1498      */
1499     exit_status =
1500 	iterate_command(iter_cmd, iter_flags, my_argv->argv, selection);
1501 
1502     argv_free(my_argv);
1503     return (exit_status);
1504 }
1505 
1506 /* list_instances - list all selected instances */
1507 
1508 static void list_instances(int iter_flags, INST_SELECTION *selection)
1509 {
1510     RING   *entry;
1511     INSTANCE *ip;
1512 
1513     /*
1514      * Iterate over the selected instances.
1515      */
1516     FOREACH_ITERATOR_INSTANCE(iter_flags, entry) {
1517 	ip = RING_TO_INSTANCE(entry);
1518 	if (match_instance_selection(ip, selection))
1519 	    vstream_printf("%-15s %-15s %-9s %s\n",
1520 			   ip->name ? ip->name : "-",
1521 			   ip->gname ? ip->gname : "-",
1522 			   ip->enabled ? "y" : "n",
1523 			   ip->config_dir);
1524     }
1525     if (vstream_fflush(VSTREAM_OUT))
1526 	msg_fatal("error writing output: %m");
1527 }
1528 
1529 /* iterate_command - execute command for selected instances */
1530 
1531 static int iterate_command(int iter_cmd, int iter_flags, char **argv,
1532 			           INST_SELECTION *selection)
1533 {
1534     int     exit_status = 0;
1535     int     matched = 0;
1536     RING   *entry;
1537     INSTANCE *ip;
1538 
1539     /*
1540      * Iterate over the selected instances.
1541      */
1542     FOREACH_ITERATOR_INSTANCE(iter_flags, entry) {
1543 	ip = RING_TO_INSTANCE(entry);
1544 	if ((iter_flags & ITER_FLAG_SKIP_DISABLED) && !ip->enabled)
1545 	    continue;
1546 	if (!match_instance_selection(ip, selection))
1547 	    continue;
1548 	matched = 1;
1549 
1550 	/* Run the requested command */
1551 	if (run_user_command(ip, iter_cmd, iter_flags, argv) != 0)
1552 	    exit_status = 1;
1553     }
1554     if (matched == 0)
1555 	msg_fatal("No matching instances");
1556 
1557     return (exit_status);
1558 }
1559 
1560 /* iterate - Iterate over all or selected instances */
1561 
1562 static NORETURN iterate(int iter_cmd, int iter_flags, int argc, char **argv,
1563 			        INST_SELECTION *selection)
1564 {
1565     int     exit_status;
1566 
1567     /*
1568      * In iterator mode, no selection means wild-card selection.
1569      */
1570     if (selection->type == INST_SEL_NONE)
1571 	selection->type = INST_SEL_ALL;
1572 
1573     /*
1574      * Load the in-memory instance table from main.cf files.
1575      */
1576     load_all_instances();
1577 
1578     /*
1579      * Iterate over the selected instances.
1580      */
1581     switch (iter_cmd) {
1582     case ITER_CMD_POSTFIX:
1583 	exit_status = iterate_postfix_command(iter_cmd, argc, argv, selection);
1584 	break;
1585     case ITER_CMD_LIST:
1586 	list_instances(iter_flags, selection);
1587 	exit_status = 0;
1588 	break;
1589     case ITER_CMD_GENERIC:
1590 	exit_status = iterate_command(iter_cmd, iter_flags, argv, selection);
1591 	break;
1592     default:
1593 	msg_panic("iterate: unknown mode: %d", iter_cmd);
1594     }
1595     exit(exit_status);
1596 }
1597 
1598 static NORETURN usage(const char *progname)
1599 {
1600     msg_fatal("Usage:"
1601 	      "%s -l [-v] [-a] [-g group] [-i instance] | "
1602 	      "%s -p [-v] [-a] [-g group] [-i instance] command... | "
1603 	      "%s -x [-v] [-a] [-i name] [-g group] command... | "
1604 	      "%s -e action [-v] [-a] [-i name] [-g group] [-I name] "
1605 	      "[-G group] [param=value ...]",
1606 	      progname, progname, progname, progname);
1607 }
1608 
1609 MAIL_VERSION_STAMP_DECLARE;
1610 
1611 /* main - iterate commands over multiple instance or manage instances */
1612 
1613 int     main(int argc, char **argv)
1614 {
1615     int     fd;
1616     struct stat st;
1617     char   *slash;
1618     char   *config_dir;
1619     int     ch;
1620     static const CONFIG_STR_TABLE str_table[] = {
1621 	VAR_MULTI_START_CMDS, DEF_MULTI_START_CMDS, &var_multi_start_cmds, 0, 0,
1622 	VAR_MULTI_STOP_CMDS, DEF_MULTI_STOP_CMDS, &var_multi_stop_cmds, 0, 0,
1623 	VAR_MULTI_CNTRL_CMDS, DEF_MULTI_CNTRL_CMDS, &var_multi_cntrl_cmds, 0, 0,
1624 	0,
1625     };
1626     int     instance_select_count = 0;
1627     int     command_mode_count = 0;
1628     INST_SELECTION selection;
1629     NAME_ASSIGNMENT assignment;
1630     int     iter_flags = ITER_FLAG_DEFAULT;
1631     int     cmd_mode = 0;
1632     int     code;
1633 
1634     selection.type = INST_SEL_NONE;
1635     assignment.name = assignment.gname = 0;
1636 
1637     /*
1638      * Fingerprint executables and core dumps.
1639      */
1640     MAIL_VERSION_STAMP_ALLOCATE;
1641 
1642     /*
1643      * Be consistent with file permissions.
1644      */
1645     umask(022);
1646 
1647     /*
1648      * To minimize confusion, make sure that the standard file descriptors
1649      * are open before opening anything else. XXX Work around for 44BSD where
1650      * fstat can return EBADF on an open file descriptor.
1651      */
1652     for (fd = 0; fd < 3; fd++)
1653 	if (fstat(fd, &st) == -1
1654 	    && (close(fd), open("/dev/null", O_RDWR, 0)) != fd)
1655 	    msg_fatal("open /dev/null: %m");
1656 
1657     /*
1658      * Set up diagnostics. XXX What if stdin is the system console during
1659      * boot time? It seems a bad idea to log startup errors to the console.
1660      * This is UNIX, a system that can run without hand holding.
1661      */
1662     if ((slash = strrchr(argv[0], '/')) != 0 && slash[1])
1663 	argv[0] = slash + 1;
1664     if (isatty(STDERR_FILENO))
1665 	msg_vstream_init(argv[0], VSTREAM_ERR);
1666     msg_syslog_init(argv[0], LOG_PID, LOG_FACILITY);
1667 
1668     /*
1669      * Check the Postfix library version as soon as we enable logging.
1670      */
1671     MAIL_VERSION_CHECK;
1672 
1673     if ((config_dir = getenv(CONF_ENV_PATH)) != 0
1674 	&& strcmp(config_dir, DEF_CONFIG_DIR) != 0)
1675 	msg_fatal("Non-default configuration directory: %s=%s",
1676 		  CONF_ENV_PATH, config_dir);
1677 
1678     /*
1679      * Parse switches.
1680      */
1681     while ((ch = GETOPT(argc, argv, "ae:g:i:G:I:lpRvx")) > 0) {
1682 	switch (ch) {
1683 	default:
1684 	    usage(argv[0]);
1685 	    /* NOTREACHED */
1686 	case 'a':
1687 	    if (selection.type != INST_SEL_ALL)
1688 		instance_select_count++;
1689 	    selection.type = INST_SEL_ALL;
1690 	    break;
1691 	case 'e':
1692 	    if ((code = EDIT_CMD_CODE(optarg)) < 0)
1693 		msg_fatal("Invalid '-e' edit action '%s'. Specify '%s', "
1694 			  "'%s', '%s', '%s', '%s', '%s', '%s', '%s' or '%s'",
1695 			  optarg,
1696 			  EDIT_CMD_STR(EDIT_CMD_CREATE),
1697 			  EDIT_CMD_STR(EDIT_CMD_DESTROY),
1698 			  EDIT_CMD_STR(EDIT_CMD_IMPORT),
1699 			  EDIT_CMD_STR(EDIT_CMD_DEPORT),
1700 			  EDIT_CMD_STR(EDIT_CMD_ENABLE),
1701 			  EDIT_CMD_STR(EDIT_CMD_DISABLE),
1702 			  EDIT_CMD_STR(EDIT_CMD_ASSIGN),
1703 			  EDIT_CMD_STR(EDIT_CMD_INIT),
1704 			  optarg);
1705 	    if (cmd_mode != code)
1706 		command_mode_count++;
1707 	    cmd_mode = code;
1708 	    break;
1709 	case 'g':
1710 	    instance_select_count++;
1711 	    selection.type = INST_SEL_GROUP;
1712 	    selection.name = optarg;
1713 	    break;
1714 	case 'i':
1715 	    instance_select_count++;
1716 	    selection.type = INST_SEL_NAME;
1717 	    selection.name = optarg;
1718 	    break;
1719 	case 'G':
1720 	    if (assignment.gname != 0)
1721 		msg_fatal("Specify at most one '-G' option");
1722 	    assignment.gname = strcmp(optarg, "-") == 0 ? "" : optarg;
1723 	    break;
1724 	case 'I':
1725 	    if (assignment.name != 0)
1726 		msg_fatal("Specify at most one '-I' option");
1727 	    assignment.name = strcmp(optarg, "-") == 0 ? "" : optarg;
1728 	    break;
1729 	case 'l':
1730 	    if (cmd_mode != ITER_CMD_LIST)
1731 		command_mode_count++;
1732 	    cmd_mode = ITER_CMD_LIST;
1733 	    break;
1734 	case 'p':
1735 	    if (cmd_mode != ITER_CMD_POSTFIX)
1736 		command_mode_count++;
1737 	    cmd_mode = ITER_CMD_POSTFIX;
1738 	    break;
1739 	case 'R':
1740 	    iter_flags ^= ITER_FLAG_REVERSE;
1741 	    break;
1742 	case 'v':
1743 	    msg_verbose++;
1744 	    check_setenv(CONF_ENV_VERB, "");
1745 	    break;
1746 	case 'x':
1747 	    if (cmd_mode != ITER_CMD_GENERIC)
1748 		command_mode_count++;
1749 	    cmd_mode = ITER_CMD_GENERIC;
1750 	    break;
1751 	}
1752     }
1753 
1754     /*
1755      * Report missing arguments, or wrong arguments in the wrong context.
1756      */
1757     if (instance_select_count > 1)
1758 	msg_fatal("Specity no more than one of '-a', '-g', '-i'");
1759 
1760     if (command_mode_count != 1)
1761 	msg_fatal("Specify exactly one of '-e', '-l', '-p', '-x'");
1762 
1763     if (cmd_mode == ITER_CMD_LIST && argc > optind)
1764 	msg_fatal("Command not allowed with '-l'");
1765 
1766     if (cmd_mode == ITER_CMD_POSTFIX || cmd_mode == ITER_CMD_GENERIC)
1767 	if (argc == optind)
1768 	    msg_fatal("Command required with '-p' or '-x' option");
1769 
1770     if (cmd_mode == ITER_CMD_POSTFIX || (cmd_mode & EDIT_CMD_MASK_ALL))
1771 	if (iter_flags != ITER_FLAG_DEFAULT)
1772 	    msg_fatal("The '-p' and '-e' options preclude the use of '-R'");
1773 
1774     if ((cmd_mode & EDIT_CMD_MASK_ASSIGN) == 0
1775 	&& (assignment.name || assignment.gname)) {
1776 	if ((cmd_mode & EDIT_CMD_MASK_ALL) == 0)
1777 	    msg_fatal("Cannot assign instance name or group without '-e %s'",
1778 		      EDIT_CMD_STR(EDIT_CMD_ASSIGN));
1779 	else
1780 	    msg_fatal("Cannot assign instance name or group with '-e %s'",
1781 		      EDIT_CMD_STR(cmd_mode));
1782     }
1783     if (cmd_mode & EDIT_CMD_MASK_ALL) {
1784 	if (cmd_mode == EDIT_CMD_ASSIGN
1785 	    && (assignment.name == 0 && assignment.gname == 0))
1786 	    msg_fatal("Specify new instance name or group with '-e %s'",
1787 		      EDIT_CMD_STR(cmd_mode));
1788 
1789 	if ((cmd_mode & ~EDIT_CMD_MASK_ADD) != 0 && argc > optind)
1790 	    msg_fatal("Parameter overrides not valid with '-e %s'",
1791 		      EDIT_CMD_STR(cmd_mode));
1792     }
1793 
1794     /*
1795      * Proces main.cf parameters.
1796      */
1797     mail_conf_read();
1798     get_mail_conf_str_table(str_table);
1799 
1800     /*
1801      * Sanity checks.
1802      */
1803     check_shared_dir_status();
1804 
1805     /*
1806      * Iterate over selected instances, or manipulate one instance.
1807      */
1808     if (cmd_mode & ITER_CMD_MASK_ALL)
1809 	iterate(cmd_mode, iter_flags, argc - optind, argv + optind, &selection);
1810     else
1811 	manage(cmd_mode, argc - optind, argv + optind, &selection, &assignment);
1812 }
1813