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