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