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