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