7 /* Postfix multi-instance manager
10 /* \fBpostmulti\fR \fB-l\fR [\fB-aRv\fR] [\fB-g \fIgroup\fR]
13 /* \fBpostmulti\fR \fB-p\fR [\fB-av\fR] [\fB-g \fIgroup\fR]
14 /* [\fB-i \fIname\fR] \fIcommand...\fR
16 /* \fBpostmulti\fR \fB-x\fR [\fB-aRv\fR] [\fB-g \fIgroup\fR]
17 /* [\fB-i \fIname\fR] \fIcommand...\fR
19 /* \fBpostmulti\fR \fB-e init\fR [\fB-v\fR]
21 /* \fBpostmulti\fR \fB-e create\fR [\fB-av\fR]
22 /* [\fB-g \fIgroup\fR] [\fB-i \fIname\fR] [\fB-G \fIgroup\fR]
23 /* [\fB-I \fIname\fR] [\fIparam=value\fR ...]
25 /* \fBpostmulti\fR \fB-e import\fR [\fB-av\fR]
26 /* [\fB-g \fIgroup\fR] [\fB-i \fIname\fR] [\fB-G \fIgroup\fR]
27 /* [\fB-I \fIname\fR] [\fBconfig_directory=\fI/path\fR]
29 /* \fBpostmulti\fR \fB-e destroy\fR [\fB-v\fR] \fB-i \fIname\fR
31 /* \fBpostmulti\fR \fB-e deport\fR [\fB-v\fR] \fB-i \fIname\fR
33 /* \fBpostmulti\fR \fB-e enable\fR [\fB-v\fR] \fB-i \fIname\fR
35 /* \fBpostmulti\fR \fB-e disable\fR [\fB-v\fR] \fB-i \fIname\fR
37 /* \fBpostmulti\fR \fB-e assign\fR [\fB-v\fR] \fB-i \fIname\fR
38 /* [\fB-I \fIname\fR] [-G \fIgroup\fR]
40 /* The \fBpostmulti\fR(1) command allows a Postfix administrator
41 /* to manage multiple Postfix instances on a single host.
43 /* \fBpostmulti\fR(1) implements two fundamental modes of
44 /* operation. In \fBiterator\fR mode, it executes the same
45 /* command for multiple Postfix instances. In \fBlife-cycle
46 /* management\fR mode, it adds or deletes one instance, or
47 /* changes the multi-instance status of one instance.
49 /* Each mode of operation has its own command syntax. For this
50 /* reason, each mode is documented in separate sections below.
54 /* A multi-instance configuration consists of one primary
55 /* Postfix instance, and one or more secondary instances whose
56 /* configuration directory pathnames are recorded in the primary
57 /* instance's main.cf file. Postfix instances share program
58 /* files and documentation, but have their own configuration,
59 /* queue and data directories.
61 /* Currently, only the default Postfix instance can be used
62 /* as primary instance in a multi-instance configuration. The
63 /* \fBpostmulti\fR(1) command does not currently support a \fB-c\fR
64 /* option to select an alternative primary instance, and exits
65 /* with a fatal error if the \fBMAIL_CONFIG\fR environment
66 /* variable is set to a non-default configuration directory.
68 /* See the MULTI_INSTANCE_README tutorial for a more detailed
69 /* discussion of multi-instance management with \fBpostmulti\fR(1).
73 /* In iterator mode, \fBpostmulti\fR performs the same operation
74 /* on all Postfix instances in turn.
76 /* If multi-instance support is not enabled, the requested
77 /* command is performed just for the primary instance.
79 /* Iterator mode implements the following command options:
80 /* .SH "Instance selection"
82 /* Perform the operation on all instances. This is the default.
83 /* .IP "\fB-g \fIgroup\fR"
84 /* Perform the operation only for members of the named \fIgroup\fR.
85 /* .IP "\fB-i \fIname\fR"
86 /* Perform the operation only for the instance with the specified
87 /* \fIname\fR. You can specify either the instance name
88 /* or the absolute pathname of the instance's configuration
89 /* directory. Specify "-" to select the primary Postfix instance.
91 /* Reverse the iteration order. This may be appropriate when
92 /* updating a multi-instance system, where "sink" instances
93 /* are started before "source" instances.
95 /* This option cannot be used with \fB-p\fR.
98 /* List Postfix instances with their instance name, instance
99 /* group name, enable/disable status and configuration directory.
100 /* .SH "Postfix-wrapper mode"
102 /* Invoke \fBpostfix(1)\fR to execute the specified \fIcommand\fR.
103 /* This option implements the \fBpostfix-wrapper\fR(5) interface.
106 /* With "start"-like commands, "postfix check" is executed for
107 /* instances that are not enabled. The full list of commands
108 /* is specified with the postmulti_start_commands parameter.
110 /* With "stop"-like commands, the iteration order is reversed,
111 /* and disabled instances are skipped. The full list of commands
112 /* is specified with the postmulti_stop_commands parameter.
114 /* With "reload" and other commands that require a started
115 /* instance, disabled instances are skipped. The full list of
116 /* commands is specified with the postmulti_control_commands
119 /* With "status" and other commands that don't require a started
120 /* instance, the command is executed for all instances.
123 /* The \fB-p\fR option can also be used interactively to
124 /* start/stop/etc. a named instance or instance group. For
125 /* example, to start just the instances in the group "msa",
126 /* invoke \fBpostmulti\fR(1) as follows:
129 /* # postmulti -g msa -p start
131 /* .SH "Command mode"
133 /* Execute the specified \fIcommand\fR for all Postfix instances.
134 /* The command runs with appropriate environment settings for
135 /* MAIL_CONFIG, command_directory, daemon_directory,
136 /* config_directory, queue_directory, data_directory,
137 /* multi_instance_name, multi_instance_group and
138 /* multi_instance_enable.
139 /* .SH "Other options"
141 /* Enable verbose logging for debugging purposes. Multiple
142 /* \fB-v\fR options make the software increasingly verbose.
143 /* LIFE-CYCLE MANAGEMENT MODE
146 /* With the \fB-e\fR option \fBpostmulti\fR(1) can be used to
147 /* add or delete a Postfix instance, and to manage the
148 /* multi-instance status of an existing instance.
150 /* The following options are implemented:
151 /* .SH "Existing instance selection"
153 /* When creating or importing an instance, place the new
154 /* instance at the front of the secondary instance list.
155 /* .IP "\fB-g \fIgroup\fR"
156 /* When creating or importing an instance, place the new
157 /* instance before the first secondary instance that is a
158 /* member of the specified group.
159 /* .IP "\fB-i \fIname\fR"
160 /* When creating or importing an instance, place the new
161 /* instance before the matching secondary instance.
163 /* With other life-cycle operations, apply the operation to
164 /* the named existing instance. Specify "-" to select the
165 /* primary Postfix instance.
166 /* .SH "New or existing instance name assignment"
167 /* .IP "\fB-I \fIname\fR"
168 /* Assign the specified instance \fIname\fR to an existing
169 /* instance, newly-created instance, or imported instance.
171 /* names other than "-" (which makes the instance "nameless")
172 /* must start with "postfix-". This restriction reduces the
173 /* likelihood of name collisions with system files.
174 /* .IP "\fB-G \fIgroup\fR"
175 /* Assign the specified \fIgroup\fR name to an existing instance
176 /* or to a newly created or imported instance.
177 /* .SH "Instance creation/deletion/status change"
178 /* .IP "\fB-e \fIaction\fR"
179 /* "Edit" managed instances. The following actions are supported:
182 /* This command is required before \fBpostmulti\fR(1) can be
183 /* used to manage Postfix instances. The "postmulti -e init"
184 /* command updates the primary instance's main.cf file by
189 /* multi_instance_wrapper =
190 /* ${command_directory}/postmulti -p --
191 /* multi_instance_enable = yes
195 /* You can set these by other means if you prefer.
197 /* Create a new Postfix instance and add it to the
198 /* multi_instance_directories parameter of the primary instance.
199 /* The "\fB-I \fIname\fR" option is recommended to give the
200 /* instance a short name that is used to construct default
201 /* values for the private directories of the new instance. The
202 /* "\fB-G \fIgroup\fR" option may be specified to assign the
203 /* instance to a group, otherwise, the new instance is not a
204 /* member of any groups.
206 /* The new instance main.cf is the stock main.cf with the
207 /* parameters that specify the locations of shared files cloned
208 /* from the primary instance. For "nameless" instances, you
209 /* should manually adjust "syslog_name" to yield a unique
210 /* "logtag" starting with "postfix-" that will uniquely identify
211 /* the instance in the mail logs. It is simpler to assign the
212 /* instance a short name with the "\fB-I \fIname\fR" option.
214 /* Optional "name=value" arguments specify the instance
215 /* config_directory, queue_directory and data_directory.
220 /* # postmulti -I postfix-mumble \e
221 /* -G mygroup -e create \e
222 /* config_directory=/my/config/dir \e
223 /* queue_directory=/my/queue/dir \e
224 /* data_directory=/my/data/dir
228 /* If any of these pathnames is not supplied, the program
229 /* attempts to generate the pathname by taking the corresponding
230 /* primary instance pathname, and by replacing the last pathname
231 /* component by the value of the \fB-I\fR option.
233 /* If the instance configuration directory already exists, and
234 /* contains both a main.cf and master.cf file, \fBcreate\fR
235 /* will "import" the instance as-is. For existing instances,
236 /* \fBcreate\fR and \fBimport\fR are identical.
238 /* Import an existing instance into the list of instances
239 /* managed by the \fBpostmulti\fR(1) multi-instance manager.
240 /* This adds the instance to the multi_instance_directories
241 /* list of the primary instance. If the "\fB-I \fIname\fR"
242 /* option is provided it specifies the new name for the instance
243 /* and is used to define a default location for the instance
244 /* configuration directory (as with \fBcreate\fR above). The
245 /* "\fB-G \fIgroup\fR" option may be used to assign the instance
246 /* to a group. Add a "\fBconfig_directory=\fI/path\fR" argument
247 /* to override a default pathname based on "\fB-I \fIname\fR".
249 /* Destroy a secondary Postfix instance. To be a candidate for
250 /* destruction an instance must be disabled, stopped and its
251 /* queue must not contain any messages. Attempts to destroy
252 /* the primary Postfix instance trigger a fatal error, without
253 /* destroying the instance.
255 /* The instance is removed from the primary instance main.cf
256 /* file's alternate_config_directories parameter and its data,
257 /* queue and configuration directories are cleaned of files
258 /* and directories created by the Postfix system. The main.cf
259 /* and master.cf files are removed from the configuration
260 /* directory even if they have been modified since initial
261 /* creation. Finally, the instance is "deported" from the list
262 /* of managed instances.
264 /* If other files are present in instance private directories,
265 /* the directories may not be fully removed, a warning is
266 /* logged to alert the administrator. It is expected that an
267 /* instance built using "fresh" directories via the \fBcreate\fR
268 /* action will be fully removed by the \fBdestroy\fR action
269 /* (if first disabled). If the instance configuration and queue
270 /* directories are populated with additional files (access and
271 /* rewriting tables, chroot jail content, etc.) the instance
272 /* directories will not be fully removed.
274 /* The \fBdestroy\fR action triggers potentially dangerous
275 /* file removal operations. Make sure the instance's data,
276 /* queue and configuration directories are set correctly and
277 /* do not contain any valuable files.
279 /* Deport a secondary instance from the list of managed
280 /* instances. This deletes the instance configuration directory
281 /* from the primary instance's multi_instance_directories list,
282 /* but does not remove any files or directories.
284 /* Assign a new instance name or a new group name to the
285 /* selected instance. Use "\fB-G -\fR" to specify "no group"
286 /* and "\fB-I -\fR" to specify "no name". If you choose to
287 /* make an instance "nameless", set a suitable syslog_name in
288 /* the corresponding main.cf file.
290 /* Mark the selected instance as enabled. This just sets the
291 /* multi_instance_enable parameter to "yes" in the instance's
294 /* Mark the selected instance as disabled. This means that
295 /* the instance will not be started etc. with "postfix start",
296 /* "postmulti -p start" and so on. The instance can still be
297 /* started etc. with "postfix -c config-directory start".
298 /* .SH "Other options"
300 /* Enable verbose logging for debugging purposes. Multiple
301 /* \fB-v\fR options make the software increasingly verbose.
306 /* The \fBpostmulti\fR(1) command exports the following environment
307 /* variables before executing the requested \fIcommand\fR for a given
309 /* .IP \fBMAIL_VERBOSE\fR
310 /* This is set when the -v command-line option is present.
311 /* .IP \fBMAIL_CONFIG\fR
312 /* The location of the configuration directory of the instance.
313 /* CONFIGURATION PARAMETERS
316 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
317 /* The default location of the Postfix main.cf and master.cf
318 /* configuration files.
319 /* .IP "\fBdaemon_directory (see 'postconf -d' output)\fR"
320 /* The directory with Postfix support programs and daemon programs.
321 /* .IP "\fBimport_environment (see 'postconf -d' output)\fR"
322 /* The list of environment parameters that a Postfix process will
323 /* import from a non-Postfix parent process.
324 /* .IP "\fBmulti_instance_directories (empty)\fR"
325 /* An optional list of non-default Postfix configuration directories;
326 /* these directories belong to additional Postfix instances that share
327 /* the Postfix executable files and documentation with the default
328 /* Postfix instance, and that are started, stopped, etc., together
329 /* with the default Postfix instance.
330 /* .IP "\fBmulti_instance_group (empty)\fR"
331 /* The optional instance group name of this Postfix instance.
332 /* .IP "\fBmulti_instance_name (empty)\fR"
333 /* The optional instance name of this Postfix instance.
334 /* .IP "\fBmulti_instance_enable (no)\fR"
335 /* Allow this Postfix instance to be started, stopped, etc., by a
336 /* multi-instance manager.
337 /* .IP "\fBpostmulti_start_commands (start)\fR"
338 /* The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager treats
339 /* as "start" commands.
340 /* .IP "\fBpostmulti_stop_commands (see 'postconf -d' output)\fR"
341 /* The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager treats
342 /* as "stop" commands.
343 /* .IP "\fBpostmulti_control_commands (reload flush)\fR"
344 /* The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager
345 /* treats as "control" commands, that operate on running instances.
346 /* .IP "\fBsyslog_facility (mail)\fR"
347 /* The syslog facility of Postfix logging.
348 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
349 /* The mail system name that is prepended to the process name in syslog
350 /* records, so that "smtpd" becomes, for example, "postfix/smtpd".
352 /* $daemon_directory/main.cf, stock configuration file
353 /* $daemon_directory/master.cf, stock configuration file
354 /* $daemon_directory/postmulti-script, life-cycle helper program
356 /* postfix(1), Postfix control program
357 /* postfix-wrapper(5), Postfix multi-instance API
359 /* Use "\fBpostconf readme_directory\fR" or "\fBpostconf
360 /* html_directory\fR" to locate this information.
361 /* MULTI_INSTANCE_README, Postfix multi-instance management
365 /* The \fBpostmulti\fR(1) command was introduced with Postfix
370 /* The Secure Mailer license must be distributed with this software.
376 /* IBM T.J. Watson Research
378 /* Yorktown Heights, NY 10598, USA
381 /* System library. */
383 #include <sys_defs.h>
384 #include <sys/stat.h>
385 #include <sys/wait.h>
399 /* Utility library. */
402 #include <msg_vstream.h>
403 #include <msg_syslog.h>
405 #include <vstring_vstream.h>
406 #include <stringops.h>
407 #include <clean_env.h>
410 #include <mymalloc.h>
412 #include <name_code.h>
415 /* Global library. */
417 #include <mail_version.h>
418 #include <mail_params.h>
419 #include <mail_conf.h>
421 /* Application-specific. */
424 * Configuration parameters, specific to postmulti(1).
426 char *var_multi_start_cmds
;
427 char *var_multi_stop_cmds
;
428 char *var_multi_cntrl_cmds
;
431 * Shared directory pathnames.
434 const char *param_name
;
438 static SHARED_PATH shared_dir_table
[] = {
439 VAR_COMMAND_DIR
, &var_command_dir
,
440 VAR_DAEMON_DIR
, &var_daemon_dir
,
447 #define ITER_CMD_POSTFIX (1<<0) /* postfix(1) iterator mode */
448 #define ITER_CMD_LIST (1<<1) /* listing iterator mode */
449 #define ITER_CMD_GENERIC (1<<2) /* generic command iterator mode */
451 #define ITER_CMD_MASK_ALL \
452 (ITER_CMD_POSTFIX | ITER_CMD_LIST | ITER_CMD_GENERIC)
454 #define EDIT_CMD_CREATE (1<<4) /* create new instance */
455 #define EDIT_CMD_IMPORT (1<<5) /* import existing instance */
456 #define EDIT_CMD_DESTROY (1<<6) /* destroy instance */
457 #define EDIT_CMD_DEPORT (1<<7) /* export instance */
458 #define EDIT_CMD_ENABLE (1<<8) /* enable start/stop */
459 #define EDIT_CMD_DISABLE (1<<9) /* disable start/stop */
460 #define EDIT_CMD_ASSIGN (1<<10) /* assign name/group */
461 #define EDIT_CMD_INIT (1<<11) /* hook into main.cf */
463 #define EDIT_CMD_MASK_ADD (EDIT_CMD_CREATE | EDIT_CMD_IMPORT)
464 #define EDIT_CMD_MASK_DEL (EDIT_CMD_DESTROY | EDIT_CMD_DEPORT)
465 #define EDIT_CMD_MASK_ASSIGN (EDIT_CMD_MASK_ADD | EDIT_CMD_ASSIGN)
466 #define EDIT_CMD_MASK_ENB (EDIT_CMD_ENABLE | EDIT_CMD_DISABLE)
467 #define EDIT_CMD_MASK_ALL \
468 (EDIT_CMD_MASK_ASSIGN | EDIT_CMD_MASK_DEL | EDIT_CMD_MASK_ENB | \
472 * Edit command to number mapping, and vice versa.
474 static NAME_CODE edit_command_table
[] = {
475 "create", EDIT_CMD_CREATE
,
476 "import", EDIT_CMD_IMPORT
,
477 "destroy", EDIT_CMD_DESTROY
,
478 "deport", EDIT_CMD_DEPORT
,
479 "enable", EDIT_CMD_ENABLE
,
480 "disable", EDIT_CMD_DISABLE
,
481 "assign", EDIT_CMD_ASSIGN
,
482 "init", EDIT_CMD_INIT
,
486 #define EDIT_CMD_CODE(str) \
487 name_code(edit_command_table, NAME_CODE_FLAG_STRICT_CASE, (str))
488 #define EDIT_CMD_STR(code) str_name_code(edit_command_table, (code))
491 * Mandatory prefix for non-empty instance names.
494 #define NAME_PREFIX "postfix-"
496 #define HAS_NAME_PREFIX(name) \
497 (strncmp((name), NAME_PREFIX, sizeof(NAME_PREFIX)-1) == 0)
498 #define NEED_NAME_PREFIX(name) \
499 ((name) != 0 && strcmp((name), "-") != 0 && !HAS_NAME_PREFIX(name))
500 #define NAME_SUFFIX(name) ((name) + sizeof(NAME_PREFIX) - 1)
503 * In-core instance structure. Only private information is kept here.
505 typedef struct instance
{
506 RING ring
; /* linkage. */
507 char *config_dir
; /* private */
508 char *queue_dir
; /* private */
509 char *data_dir
; /* private */
510 char *name
; /* null or name */
511 char *gname
; /* null or group */
512 int enabled
; /* start/stop enable */
513 int primary
; /* special */
517 * Managed instance list (edit mode and iterator mode).
519 static RING instance_hd
[1]; /* instance list head */
521 #define RING_TO_INSTANCE(ring_ptr) RING_TO_APPL(ring_ptr, INSTANCE, ring)
522 #define RING_PTR_OF(x) (&((x)->ring))
524 #define FOREACH_INSTANCE(entry) \
525 for ((entry) = instance_hd; \
526 ((entry) = ring_succ(entry)) != instance_hd;)
528 #define FOREACH_SECONDARY_INSTANCE(entry) \
529 for ((entry) = ring_succ(instance_hd); \
530 ((entry) = ring_succ(entry)) != instance_hd;)
532 #define NEXT_ITERATOR_INSTANCE(flags, entry) \
533 (((flags) & ITER_FLAG_REVERSE) ? ring_pred(entry) : ring_succ(entry))
535 #define FOREACH_ITERATOR_INSTANCE(flags, entry) \
536 for ((entry) = instance_hd; \
537 ((entry) = NEXT_ITERATOR_INSTANCE(flags, (entry))) != instance_hd;)
540 * Instance selection. One can either select all instances, select by
541 * instance name, or select by instance group.
544 int type
; /* see below */
545 char *name
; /* undefined or name */
548 #define INST_SEL_NONE 0 /* default: no selection */
549 #define INST_SEL_ALL 1 /* select all instances */
550 #define INST_SEL_NAME 2 /* select instance name */
551 #define INST_SEL_GROUP 3 /* select instance group */
554 * Instance name assignment. Each instance may be assigned an instance name
555 * (this must be globally unique within a multi-instance cluster) or an
556 * instance group name (this is intended to be shared). Externally, empty
557 * names may be represented as "-". Internally, we use "" only, to simplify
561 char *name
; /* null or assigned instance name */
562 char *gname
; /* null or assigned group name */
566 * Iterator controls for non-edit commands. One can reverse the iteration
567 * order, or give special treatment to disabled instances.
569 #define ITER_FLAG_DEFAULT 0 /* default setting */
570 #define ITER_FLAG_REVERSE (1<<0) /* reverse iteration order */
571 #define ITER_FLAG_CHECK_DISABLED (1<<1) /* check disabled instances */
572 #define ITER_FLAG_SKIP_DISABLED (1<<2) /* skip disabled instances */
575 * Environment export controls for edit commands. postmulti(1) exports only
576 * things that need to be updated.
578 #define EXP_FLAG_MULTI_DIRS (1<<0) /* export multi_instance_directories */
579 #define EXP_FLAG_MULTI_NAME (1<<1) /* export multi_instance_name */
580 #define EXP_FLAG_MULTI_GROUP (1<<2) /* export multi_instance_group */
583 * To detect conflicts, each instance name and each shared or private
584 * pathname is registered in one place, with its owner. Everyone must
585 * register their claims when they join, and will be rejected in case of
588 * Each claim value involves a parameter value (either a directory name or an
589 * instance name). Each claim owner is the config_directory pathname plus
590 * the parameter name.
592 * XXX: No multi.cf lock file, so this is not race-free.
594 static HTABLE
*claim_table
;
596 #define IS_CLAIMED_BY(name) \
597 (claim_table ? htable_find(claim_table, (name)) : 0)
600 * Forward references.
602 static int iterate_command(int, int, char **, INST_SELECTION
*);
603 static int match_instance_selection(INSTANCE
*, INST_SELECTION
*);
608 #define INSTANCE_NAME(i) ((i)->name ? (i)->name : (i)->config_dir)
609 #define STR(buf) vstring_str(buf)
611 /* register_claim - register claim or bust */
613 static void register_claim(const char *instance_path
, const char *param_name
,
614 const char *param_value
)
616 const char *myname
= "register_claim";
623 if (instance_path
== 0 || *instance_path
== 0)
624 msg_panic("%s: no or empty instance pathname", myname
);
625 if (param_name
== 0 || *param_name
== 0)
626 msg_panic("%s: no or empty parameter name", myname
);
627 if (param_value
== 0)
628 msg_panic("%s: no parameter value", myname
);
631 * Make a claim or report a conflict.
633 if (claim_table
== 0)
634 claim_table
= htable_create(100);
635 requestor
= concatenate(instance_path
, ", ", param_name
, (char *) 0);
636 if ((owner
= htable_find(claim_table
, param_value
)) == 0) {
637 (void) htable_enter(claim_table
, param_value
, requestor
);
638 } else if (strcmp(owner
, requestor
) == 0) {
641 msg_fatal("instance %s, %s=%s conflicts with instance %s=%s",
642 instance_path
, param_name
, param_value
, owner
, param_value
);
646 /* claim_instance_attributes - claim multiple private instance attributes */
648 static void claim_instance_attributes(INSTANCE
*ip
)
652 * Detect instance name or pathname conflicts between this instance and
653 * other instances. XXX: No multi.cf lock file, so this is not race-free.
656 register_claim(ip
->config_dir
, VAR_MULTI_NAME
, ip
->name
);
657 register_claim(ip
->config_dir
, VAR_CONFIG_DIR
, ip
->config_dir
);
658 register_claim(ip
->config_dir
, VAR_QUEUE_DIR
, ip
->queue_dir
);
659 register_claim(ip
->config_dir
, VAR_DATA_DIR
, ip
->data_dir
);
662 /* alloc_instance - allocate a single instance object */
664 static INSTANCE
*alloc_instance(const char *config_dir
)
666 INSTANCE
*ip
= (INSTANCE
*) mymalloc(sizeof(INSTANCE
));
668 ring_init(RING_PTR_OF(ip
));
669 ip
->config_dir
= config_dir
? mystrdup(config_dir
) : 0;
682 /* free_instance - free a single instance object */
684 static void free_instance(INSTANCE
*ip
)
688 * If we continue after secondary main.cf file read error, we must be
689 * prepared for the case that some parameters may be missing.
696 myfree(ip
->config_dir
);
698 myfree(ip
->queue_dir
);
700 myfree(ip
->data_dir
);
706 /* insert_instance - insert instance before selected location, claim names */
708 static void insert_instance(INSTANCE
*ip
, INST_SELECTION
*selection
)
712 #define append_instance(ip) insert_instance((ip), (INST_SELECTION *) 0)
715 * Insert instance before the selected site.
717 claim_instance_attributes(ip
);
718 if (ring_succ(instance_hd
) == 0)
719 ring_init(instance_hd
);
720 if (selection
&& selection
->type
!= INST_SEL_NONE
) {
721 FOREACH_SECONDARY_INSTANCE(old
) {
722 if (match_instance_selection(RING_TO_INSTANCE(old
), selection
)) {
723 ring_prepend(old
, RING_PTR_OF(ip
));
727 if (selection
->type
!= INST_SEL_ALL
)
728 msg_fatal("No matching secondary instances");
730 ring_prepend(instance_hd
, RING_PTR_OF(ip
));
733 /* create_primary_instance - synthetic entry for primary instance */
735 static INSTANCE
*create_primary_instance(void)
737 INSTANCE
*ip
= alloc_instance(var_config_dir
);
740 * There is no need to load primary instance paramater settings from
741 * file. We already have the main.cf parameters of interest in memory.
743 #define SAVE_INSTANCE_NAME(val) (*(val) ? mystrdup(val) : 0)
745 ip
->name
= SAVE_INSTANCE_NAME(var_multi_name
);
746 ip
->gname
= SAVE_INSTANCE_NAME(var_multi_group
);
747 ip
->enabled
= var_multi_enable
;
748 ip
->queue_dir
= mystrdup(var_queue_dir
);
749 ip
->data_dir
= mystrdup(var_data_dir
);
754 /* load_instance - read instance parameters from config_dir/main.cf */
756 static INSTANCE
*load_instance(INSTANCE
*ip
)
764 static NAME_CODE bool_code
[] = {
771 * XXX: We could really use a "postconf -E" to expand values in the
772 * context of the target main.cf!
774 #define REQUEST_PARAM_COUNT 5 /* # of requested parameters */
776 cmd
= argv_alloc(REQUEST_PARAM_COUNT
+ 3);
777 name
= concatenate(var_command_dir
, "/", "postconf", (char *) 0);
778 argv_add(cmd
, name
, "-c", ip
->config_dir
,
779 VAR_QUEUE_DIR
, VAR_DATA_DIR
,
780 VAR_MULTI_NAME
, VAR_MULTI_GROUP
, VAR_MULTI_ENABLE
,
783 pipe
= vstream_popen(O_RDONLY
, VSTREAM_POPEN_ARGV
, cmd
->argv
,
787 msg_fatal("Cannot parse %s/main.cf file: %m", ip
->config_dir
);
790 * Read parameter settings from postconf. See also comments below on
791 * whether we should continue or skip groups after error instead of
792 * bailing out immediately.
794 buf
= vstring_alloc(100);
795 while (vstring_get_nonl(buf
, pipe
) != VSTREAM_EOF
) {
796 if (split_nameval(STR(buf
), &name
, &value
))
797 msg_fatal("Invalid %s/main.cf parameter: %s",
798 ip
->config_dir
, STR(buf
));
799 if (strcmp(name
, VAR_QUEUE_DIR
) == 0 && ++count
)
800 ip
->queue_dir
= mystrdup(value
);
801 else if (strcmp(name
, VAR_DATA_DIR
) == 0 && ++count
)
802 ip
->data_dir
= mystrdup(value
);
803 else if (strcmp(name
, VAR_MULTI_NAME
) == 0 && ++count
)
804 ip
->name
= SAVE_INSTANCE_NAME(value
);
805 else if (strcmp(name
, VAR_MULTI_GROUP
) == 0 && ++count
)
806 ip
->gname
= SAVE_INSTANCE_NAME(value
);
807 else if (strcmp(name
, VAR_MULTI_ENABLE
) == 0 && ++count
) {
808 /* mail_conf_bool(3) is case insensitive! */
809 ip
->enabled
= name_code(bool_code
, NAME_CODE_FLAG_NONE
, value
);
811 msg_fatal("Unexpected %s/main.cf entry: %s = %s",
812 ip
->config_dir
, VAR_MULTI_ENABLE
, value
);
818 * XXX We should not bail out while reading a bad secondary main.cf file.
819 * When we manage dozens or more instances, the likelihood increases that
820 * some file will be damaged or missing after a system crash. That is not
821 * a good reason to prevent undamaged Postfix instances from starting.
823 if (count
!= REQUEST_PARAM_COUNT
)
824 msg_fatal("Failed to obtain all required %s/main.cf parameters",
827 if (vstream_pclose(pipe
))
828 msg_fatal("Cannot parse %s/main.cf file", ip
->config_dir
);
832 /* load_all_instances - compute list of Postfix instances */
834 static void load_all_instances(void)
836 INSTANCE
*primary_instance
;
838 ARGV
*secondary_names
;
841 * Avoid unexpected behavior when $multi_instance_directories contains
842 * only comma characters. Count the actual number of elements, before we
843 * decide that the list is empty.
845 secondary_names
= argv_split(var_multi_conf_dirs
, "\t\n\r, ");
848 * First, the primary instance. This is synthesized out of thin air.
850 primary_instance
= create_primary_instance();
851 if (secondary_names
->argc
== 0)
852 primary_instance
->enabled
= 1; /* Single-instance mode */
853 append_instance(primary_instance
);
856 * Next, instances defined in $multi_instance_directories. Note:
857 * load_instance() has side effects on the global config dictionary, but
858 * this does not affect the values that have already been extracted into
861 for (cpp
= secondary_names
->argv
; *cpp
!= 0; cpp
++)
862 append_instance(load_instance(alloc_instance(*cpp
)));
864 argv_free(secondary_names
);
867 /* match_instance_selection - match all/name/group constraints */
869 static int match_instance_selection(INSTANCE
*ip
, INST_SELECTION
*selection
)
875 * When selecting (rather than assigning names) an instance, we match by
876 * the instance name, config_directory path, or the instance name suffix
877 * (name without mandatory prefix). Selecting "-" selects the primary
880 switch (selection
->type
) {
886 return (ip
->gname
!= 0 && strcmp(selection
->name
, ip
->gname
) == 0);
888 name
= selection
->name
;
889 if (*name
== '/' || ip
->name
== 0)
890 iname
= ip
->config_dir
;
891 else if (!HAS_NAME_PREFIX(name
) && HAS_NAME_PREFIX(ip
->name
))
892 iname
= NAME_SUFFIX(ip
->name
);
895 return (strcmp(name
, iname
) == 0
896 || (ip
->primary
&& strcmp(name
, "-") == 0));
898 msg_panic("match_instance_selection: unknown selection type: %d",
903 /* check_setenv - setenv() with extreme prejudice */
905 static void check_setenv(const char *name
, const char *value
)
908 if (setenv(name
, value
, CLOBBER
) < 0)
909 msg_fatal("setenv: %m");
912 /* prepend_command_path - prepend command_directory to PATH */
914 static void prepend_command_path(void)
919 * Carefully prepend "$command_directory:" to PATH. We can free the
920 * buffer after check_setenv(), since the value is copied there.
922 cmd_path
= safe_getenv("PATH");
923 cmd_path
= concatenate(var_command_dir
, ":", (cmd_path
&& *cmd_path
) ?
924 cmd_path
: ROOT_PATH
, (char *) 0);
925 check_setenv("PATH", cmd_path
);
929 /* check_shared_dir_status - check and claim shared directories */
931 static void check_shared_dir_status(void)
934 const SHARED_PATH
*sp
;
936 for (sp
= shared_dir_table
; sp
->param_name
; ++sp
) {
937 if (stat(sp
->param_value
[0], &st
) < 0)
938 msg_fatal("%s = '%s': directory not found: %m",
939 sp
->param_name
, sp
->param_value
[0]);
940 if (!S_ISDIR(st
.st_mode
))
941 msg_fatal("%s = '%s' is not a directory",
942 sp
->param_name
, sp
->param_value
[0]);
943 register_claim(var_config_dir
, sp
->param_name
, sp
->param_value
[0]);
947 /* check_safe_name - allow instance or group name with only "safe" characters */
949 static int check_safe_name(const char *s
)
951 #define SAFE_PUNCT "!@%-_=+:./"
955 if (!ISALNUM(*s
) && !strchr(SAFE_PUNCT
, *s
))
961 /* check_name_assignments - Check validity of assigned instance or group name */
963 static void check_name_assignments(NAME_ASSIGNMENT
*assignment
)
967 * Syntax check the assigned instance name. This name is also used to
968 * generate directory pathnames, so we must not allow "/" characters.
970 * The value "" will clear the name and is always valid. The command-line
971 * parser has already converted "-" into "", to simplify implementation.
973 if (assignment
->name
&& *assignment
->name
) {
974 if (!check_safe_name(assignment
->name
))
975 msg_fatal("Unsafe characters in new instance name: '%s'",
977 if (strchr(assignment
->name
, '/'))
978 msg_fatal("Illegal '/' character in new instance name: '%s'",
980 if (NEED_NAME_PREFIX(assignment
->name
))
981 msg_fatal("New instance name must start with '%s'",
986 * Syntax check the assigned group name.
988 if (assignment
->gname
&& *assignment
->gname
) {
989 if (!check_safe_name(assignment
->gname
))
990 msg_fatal("Unsafe characters in '-G %s'", assignment
->gname
);
994 /* do_name_assignments - assign instance/group names */
996 static int do_name_assignments(INSTANCE
*target
, NAME_ASSIGNMENT
*assignment
)
998 int export_flags
= 0;
1001 * The command-line parser has already converted "-" into "", to simplify
1004 if (assignment
->name
1005 && strcmp(assignment
->name
, target
->name
? target
->name
: "")) {
1006 register_claim(target
->config_dir
, VAR_MULTI_NAME
, assignment
->name
);
1008 myfree(target
->name
);
1009 target
->name
= SAVE_INSTANCE_NAME(assignment
->name
);
1010 export_flags
|= EXP_FLAG_MULTI_NAME
;
1012 if (assignment
->gname
1013 && strcmp(assignment
->gname
, target
->gname
? target
->gname
: "")) {
1015 myfree(target
->gname
);
1016 target
->gname
= SAVE_INSTANCE_NAME(assignment
->gname
);
1017 export_flags
|= EXP_FLAG_MULTI_GROUP
;
1019 return (export_flags
);
1022 /* make_private_path - generate secondary pathname using primary as template */
1024 static char *make_private_path(const char *param_name
,
1025 const char *primary_value
,
1026 NAME_ASSIGNMENT
*assignment
)
1033 * The command-line parser has already converted "-" into "", to simplify
1036 if (assignment
->name
== 0 || *assignment
->name
== 0)
1037 msg_fatal("Missing %s parameter value", param_name
);
1039 if (*primary_value
!= '/')
1040 msg_fatal("Invalid default %s parameter value: '%s': "
1041 "specify an absolute pathname",
1042 param_name
, primary_value
);
1044 base
= mystrdup(primary_value
);
1045 if ((end
= strrchr(base
, '/')) != 0) {
1046 /* Drop trailing slashes */
1047 if (end
[1] == '\0') {
1048 while (--end
> base
&& *end
== '/')
1050 end
= strrchr(base
, '/');
1052 /* Drop last path component */
1053 while (end
> base
&& *end
== '/')
1056 path
= concatenate(base
[1] ? base
: "", "/",
1057 assignment
->name
, (char *) 0);
1062 /* assign_new_parameter - assign new instance private name=value */
1064 static void assign_new_parameter(INSTANCE
*new, int edit_cmd
,
1074 * With "import", only config_directory is specified on the command line
1075 * (either explicitly as config_directory=/path/name, or implicitly as
1076 * instance name). The other private directory pathnames are taken from
1077 * the existing instance's main.cf file.
1079 * With "create", all private pathname parameters are specified on the
1080 * command line, or generated from an instance name.
1082 saved_arg
= mystrdup(arg
);
1083 if (split_nameval(saved_arg
, &name
, &value
))
1084 msg_fatal("Malformed parameter setting '%s'", arg
);
1086 if (strcmp(VAR_CONFIG_DIR
, name
) == 0) {
1087 target
= &new->config_dir
;
1088 } else if (edit_cmd
!= EDIT_CMD_IMPORT
) {
1089 if (strcmp(VAR_QUEUE_DIR
, name
) == 0) {
1090 target
= &new->queue_dir
;
1091 } else if (strcmp(VAR_DATA_DIR
, name
) == 0) {
1092 target
= &new->data_dir
;
1096 msg_fatal("Parameter '%s' not valid with action %s",
1097 name
, EDIT_CMD_STR(edit_cmd
));
1100 * Extract and assign the parameter value. We do a limited number of
1101 * checks here. Conflicts between instances are checked by the caller.
1102 * More checks may be implemented in the helper script if inspired.
1105 msg_fatal("Parameter setting '%s' is not an absolute path", name
);
1107 /* Tolerate+trim trailing "/" from readline completion */
1108 for (end
= value
+ strlen(value
) - 1; end
> value
&& *end
== '/'; --end
)
1111 /* No checks here for "/." or other shoot-foot silliness. */
1113 msg_fatal("Parameter setting '%s' is the root directory", name
);
1117 *target
= mystrdup(value
);
1125 /* assign_new_parameters - initialize new instance private parameters */
1127 static void assign_new_parameters(INSTANCE
*new, int edit_cmd
,
1128 char **argv
, NAME_ASSIGNMENT
*assignment
)
1133 * Sanity check the explicit parameter settings. More stringent checks
1134 * may take place in the helper script.
1137 assign_new_parameter(new, edit_cmd
, *argv
++);
1140 * Initialize any missing private directory pathnames, using the primary
1141 * configuration directory parameter values as a template, and using the
1142 * assigned instance name to fill in the blanks.
1144 * When importing an existing instance, load private directory pathnames
1145 * from its main.cf file.
1147 if (new->config_dir
== 0)
1149 make_private_path(VAR_CONFIG_DIR
, var_config_dir
, assignment
);
1150 /* Needed for better-quality error message. */
1151 if ((owner
= IS_CLAIMED_BY(new->config_dir
)) != 0)
1152 msg_fatal("new %s=%s is already in use by instance %s=%s",
1153 VAR_CONFIG_DIR
, new->config_dir
, owner
, new->config_dir
);
1154 if (edit_cmd
!= EDIT_CMD_IMPORT
) {
1155 if (new->queue_dir
== 0)
1157 make_private_path(VAR_QUEUE_DIR
, var_queue_dir
, assignment
);
1158 if (new->data_dir
== 0)
1160 make_private_path(VAR_DATA_DIR
, var_data_dir
, assignment
);
1166 /* export_helper_environment - update environment settings for helper command */
1168 static void export_helper_environment(INSTANCE
*target
, int export_flags
)
1171 VSTRING
*multi_dirs
;
1172 const SHARED_PATH
*sp
;
1176 * Environment import filter, to enforce consistent behavior whether this
1177 * command is started by hand, or at system boot time. This is necessary
1178 * because some shell scripts use environment settings to override
1181 import_env
= argv_split(var_import_environ
, ", \t\r\n");
1182 clean_env(import_env
->argv
);
1183 argv_free(import_env
);
1186 * Prepend $command_directory: to PATH. This supposedly ensures that
1187 * naive programs will execute commands from the right Postfix version.
1189 prepend_command_path();
1192 * The following ensures that Postfix's own programs will target the
1195 check_setenv(CONF_ENV_PATH
, var_config_dir
);
1198 * Export the parameter settings that are shared between instances.
1200 for (sp
= shared_dir_table
; sp
->param_name
; ++sp
)
1201 check_setenv(sp
->param_name
, sp
->param_value
[0]);
1204 * Export the target instance's private directory locations.
1206 check_setenv(VAR_CONFIG_DIR
, target
->config_dir
);
1207 check_setenv(VAR_QUEUE_DIR
, target
->queue_dir
);
1208 check_setenv(VAR_DATA_DIR
, target
->data_dir
);
1211 * With operations that add or delete a secondary instance, we export the
1212 * modified multi_instance_directories parameter value for the primary
1215 if (export_flags
& EXP_FLAG_MULTI_DIRS
) {
1216 multi_dirs
= vstring_alloc(100);
1217 FOREACH_SECONDARY_INSTANCE(entry
) {
1218 if (VSTRING_LEN(multi_dirs
) > 0)
1219 VSTRING_ADDCH(multi_dirs
, ' ');
1220 vstring_strcat(multi_dirs
, RING_TO_INSTANCE(entry
)->config_dir
);
1222 check_setenv(VAR_MULTI_CONF_DIRS
, STR(multi_dirs
));
1223 vstring_free(multi_dirs
);
1227 * Export updates for the instance name and group. Empty value (or no
1228 * export) means don't update, "-" means clear.
1230 if (export_flags
& EXP_FLAG_MULTI_NAME
)
1231 check_setenv(VAR_MULTI_NAME
, target
->name
&& *target
->name
?
1232 target
->name
: "-");
1234 if (export_flags
& EXP_FLAG_MULTI_GROUP
)
1235 check_setenv(VAR_MULTI_GROUP
, target
->gname
&& *target
->gname
?
1236 target
->gname
: "-");
1239 * If we would implement enable/disable commands by exporting the updated
1240 * parameter value, then we could skip commands that have no effect, just
1241 * like we can skip "assign" commands that make no change.
1245 /* install_new_instance - install and return newly created instance */
1247 static INSTANCE
*install_new_instance(int edit_cmd
, char **argv
,
1248 INST_SELECTION
*selection
,
1249 NAME_ASSIGNMENT
*assignment
,
1254 new = alloc_instance((char *) 0);
1255 check_name_assignments(assignment
);
1256 assign_new_parameters(new, edit_cmd
, argv
, assignment
);
1258 (do_name_assignments(new, assignment
) | EXP_FLAG_MULTI_DIRS
);
1259 insert_instance(new, selection
);
1263 /* update_instance - update existing instance, return export flags */
1265 static int update_instance(INSTANCE
*target
, NAME_ASSIGNMENT
*assignment
)
1269 check_name_assignments(assignment
);
1270 export_flags
= do_name_assignments(target
, assignment
);
1271 return (export_flags
);
1274 /* select_existing_instance - return instance selected for management */
1276 static INSTANCE
*select_existing_instance(INST_SELECTION
*selection
,
1280 INSTANCE
*selected
= 0;
1284 #define DONT_UNLINK 0
1287 if (selection
->type
!= INST_SEL_NAME
)
1288 msg_fatal("Select an instance via '-i name'");
1290 /* Find the selected instance and its predecessor */
1291 FOREACH_INSTANCE(entry
) {
1292 if (match_instance_selection(ip
= RING_TO_INSTANCE(entry
), selection
)) {
1299 msg_fatal("No instance named %s", selection
->name
);
1302 /* Splice the target instance out of the list */
1303 if (ring_pred(entry
) == instance_hd
)
1304 msg_fatal("Cannot remove the primary instance");
1305 if (selected
->enabled
)
1306 msg_fatal("Cannot remove enabled instances");
1308 if (export_flags
== 0)
1309 msg_panic("select_existing_instance: no export flags");
1310 *export_flags
|= EXP_FLAG_MULTI_DIRS
;
1315 /* manage - create/destroy/... manage instances */
1317 static NORETURN
manage(int edit_cmd
, int argc
, char **argv
,
1318 INST_SELECTION
*selection
,
1319 NAME_ASSIGNMENT
*assignment
)
1326 * Edit mode is not subject to iterator controls.
1328 #define NO_EXPORT_FLAGS ((int *) 0)
1333 target
= create_primary_instance();
1336 case EDIT_CMD_CREATE
:
1337 case EDIT_CMD_IMPORT
:
1338 load_all_instances();
1339 target
= install_new_instance(edit_cmd
, argv
, selection
,
1340 assignment
, &export_flags
);
1343 case EDIT_CMD_ASSIGN
:
1344 load_all_instances();
1346 select_existing_instance(selection
, DONT_UNLINK
, NO_EXPORT_FLAGS
);
1347 export_flags
|= update_instance(target
, assignment
);
1348 if (export_flags
== 0)
1352 case EDIT_CMD_DESTROY
:
1353 case EDIT_CMD_DEPORT
:
1354 load_all_instances();
1355 target
= select_existing_instance(selection
, DO_UNLINK
, &export_flags
);
1359 load_all_instances();
1361 select_existing_instance(selection
, DONT_UNLINK
, NO_EXPORT_FLAGS
);
1366 * Set up the helper script's process environment, and execute the helper
1369 #define HELPER "postmulti-script"
1371 export_helper_environment(target
, export_flags
);
1372 cmd
= concatenate(var_daemon_dir
, "/" HELPER
, (char *) 0);
1373 execl(cmd
, cmd
, "-e", EDIT_CMD_STR(edit_cmd
), (char *) 0);
1374 msg_fatal("%s: %m", cmd
);
1377 /* run_user_command - execute external command with requested MAIL_CONFIG env */
1379 static int run_user_command(INSTANCE
*ip
, int iter_cmd
, int iter_flags
,
1382 WAIT_STATUS_T status
;
1387 * Set up a process environment. The postfix(1) command needs MAIL_CONFIG
1388 * (or the equivalent command-line option); it overrides everything else.
1390 * postmulti(1) typically runs various Postfix utilities (postsuper, ...) in
1391 * the context of one or more instances. It can also run various scripts
1392 * on the users PATH. So we can't clobber the user's PATH, but do want to
1393 * make sure that the utilities in $command_directory are always found in
1394 * the right place (or at all).
1396 switch (pid
= fork()) {
1398 msg_warn("fork %s: %m", argv
[0]);
1401 check_setenv(CONF_ENV_PATH
, ip
->config_dir
);
1402 if (iter_cmd
!= ITER_CMD_POSTFIX
) {
1403 check_setenv(VAR_DAEMON_DIR
, var_daemon_dir
);
1404 check_setenv(VAR_COMMAND_DIR
, var_command_dir
);
1405 check_setenv(VAR_CONFIG_DIR
, ip
->config_dir
);
1406 check_setenv(VAR_QUEUE_DIR
, ip
->queue_dir
);
1407 check_setenv(VAR_DATA_DIR
, ip
->data_dir
);
1408 check_setenv(VAR_MULTI_NAME
, ip
->name
? ip
->name
: "");
1409 check_setenv(VAR_MULTI_GROUP
, ip
->gname
? ip
->gname
: "");
1410 check_setenv(VAR_MULTI_ENABLE
, ip
->enabled
?
1411 CONFIG_BOOL_YES
: CONFIG_BOOL_NO
);
1412 prepend_command_path();
1416 * Replace: postfix -- start ... With: postfix -- check ...
1418 if (iter_cmd
== ITER_CMD_POSTFIX
1419 && (iter_flags
& ITER_FLAG_CHECK_DISABLED
) && !ip
->enabled
)
1422 execvp(argv
[0], argv
);
1423 msg_fatal("execvp %s: %m", argv
[0]);
1426 wpid
= waitpid(pid
, &status
, 0);
1427 } while (wpid
== -1 && errno
== EINTR
);
1428 return (wpid
== -1 ? -1 :
1429 WIFEXITED(status
) ? WEXITSTATUS(status
) : 1);
1433 /* word_in_list - look up command in start, stop, or control list */
1435 static int word_in_list(char *cmdlist
, const char *cmd
)
1441 cp
= saved
= mystrdup(cmdlist
);
1442 while ((elem
= mystrtok(&cp
, "\t\n\r, ")) != 0 && strcmp(elem
, cmd
) != 0)
1448 /* iterate_postfix_command - execute postfix(1) command */
1450 static int iterate_postfix_command(int iter_cmd
, int argc
, char **argv
,
1451 INST_SELECTION
*selection
)
1459 * Override the iterator controls.
1461 if (word_in_list(var_multi_start_cmds
, argv
[0])) {
1462 iter_flags
= ITER_FLAG_CHECK_DISABLED
;
1463 } else if (word_in_list(var_multi_stop_cmds
, argv
[0])) {
1464 iter_flags
= ITER_FLAG_SKIP_DISABLED
| ITER_FLAG_REVERSE
;
1465 } else if (word_in_list(var_multi_cntrl_cmds
, argv
[0])) {
1466 iter_flags
= ITER_FLAG_SKIP_DISABLED
;
1472 * Override the command line in a straightforward manner: prepend
1473 * "postfix --" to the command arguments. Other overrides (environment,
1474 * start -> check) are implemented below the iterator.
1476 #define POSTFIX_CMD "postfix"
1478 my_argv
= argv_alloc(argc
+ 2);
1479 cmd
= concatenate(var_command_dir
, "/" POSTFIX_CMD
, (char *) 0);
1480 argv_add(my_argv
, cmd
, "--", (char *) 0);
1483 argv_add(my_argv
, *argv
++, (char *) 0);
1486 * Execute the command for all applicable Postfix instances.
1489 iterate_command(iter_cmd
, iter_flags
, my_argv
->argv
, selection
);
1492 return (exit_status
);
1495 /* list_instances - list all selected instances */
1497 static void list_instances(int iter_flags
, INST_SELECTION
*selection
)
1503 * Iterate over the selected instances.
1505 FOREACH_ITERATOR_INSTANCE(iter_flags
, entry
) {
1506 ip
= RING_TO_INSTANCE(entry
);
1507 if (match_instance_selection(ip
, selection
))
1508 vstream_printf("%-15s %-15s %-9s %s\n",
1509 ip
->name
? ip
->name
: "-",
1510 ip
->gname
? ip
->gname
: "-",
1511 ip
->enabled
? "y" : "n",
1514 if (vstream_fflush(VSTREAM_OUT
))
1515 msg_fatal("error writing output: %m");
1518 /* iterate_command - execute command for selected instances */
1520 static int iterate_command(int iter_cmd
, int iter_flags
, char **argv
,
1521 INST_SELECTION
*selection
)
1523 int exit_status
= 0;
1529 * Iterate over the selected instances.
1531 FOREACH_ITERATOR_INSTANCE(iter_flags
, entry
) {
1532 ip
= RING_TO_INSTANCE(entry
);
1533 if (!match_instance_selection(ip
, selection
))
1537 /* Run the requested command */
1538 if (run_user_command(ip
, iter_cmd
, iter_flags
, argv
) != 0)
1542 msg_fatal("No matching instances");
1544 return (exit_status
);
1547 /* iterate - Iterate over all or selected instances */
1549 static NORETURN
iterate(int iter_cmd
, int iter_flags
, int argc
, char **argv
,
1550 INST_SELECTION
*selection
)
1555 * In iterator mode, no selection means wild-card selection.
1557 if (selection
->type
== INST_SEL_NONE
)
1558 selection
->type
= INST_SEL_ALL
;
1561 * Load the in-memory instance table from main.cf files.
1563 load_all_instances();
1566 * Iterate over the selected instances.
1569 case ITER_CMD_POSTFIX
:
1570 exit_status
= iterate_postfix_command(iter_cmd
, argc
, argv
, selection
);
1573 list_instances(iter_flags
, selection
);
1576 case ITER_CMD_GENERIC
:
1577 exit_status
= iterate_command(iter_cmd
, iter_flags
, argv
, selection
);
1580 msg_panic("iterate: unknown mode: %d", iter_cmd
);
1585 static NORETURN
usage(const char *progname
)
1588 "%s -l [-v] [-a] [-g group] [-i instance] | "
1589 "%s -p [-v] [-a] [-g group] [-i instance] command... | "
1590 "%s -x [-v] [-a] [-i name] [-g group] command... | "
1591 "%s -e action [-v] [-a] [-i name] [-g group] [-I name] "
1592 "[-G group] [param=value ...]",
1593 progname
, progname
, progname
, progname
);
1596 MAIL_VERSION_STAMP_DECLARE
;
1598 /* main - iterate commands over multiple instance or manage instances */
1600 int main(int argc
, char **argv
)
1607 static const CONFIG_STR_TABLE str_table
[] = {
1608 VAR_MULTI_START_CMDS
, DEF_MULTI_START_CMDS
, &var_multi_start_cmds
, 0, 0,
1609 VAR_MULTI_STOP_CMDS
, DEF_MULTI_STOP_CMDS
, &var_multi_stop_cmds
, 0, 0,
1610 VAR_MULTI_CNTRL_CMDS
, DEF_MULTI_CNTRL_CMDS
, &var_multi_cntrl_cmds
, 0, 0,
1613 int instance_select_count
= 0;
1614 int command_mode_count
= 0;
1615 INST_SELECTION selection
;
1616 NAME_ASSIGNMENT assignment
;
1617 int iter_flags
= ITER_FLAG_DEFAULT
;
1621 selection
.type
= INST_SEL_NONE
;
1622 assignment
.name
= assignment
.gname
= 0;
1625 * Fingerprint executables and core dumps.
1627 MAIL_VERSION_STAMP_ALLOCATE
;
1630 * Be consistent with file permissions.
1635 * To minimize confusion, make sure that the standard file descriptors
1636 * are open before opening anything else. XXX Work around for 44BSD where
1637 * fstat can return EBADF on an open file descriptor.
1639 for (fd
= 0; fd
< 3; fd
++)
1640 if (fstat(fd
, &st
) == -1
1641 && (close(fd
), open("/dev/null", O_RDWR
, 0)) != fd
)
1642 msg_fatal("open /dev/null: %m");
1645 * Set up diagnostics. XXX What if stdin is the system console during
1646 * boot time? It seems a bad idea to log startup errors to the console.
1647 * This is UNIX, a system that can run without hand holding.
1649 if ((slash
= strrchr(argv
[0], '/')) != 0 && slash
[1])
1650 argv
[0] = slash
+ 1;
1651 if (isatty(STDERR_FILENO
))
1652 msg_vstream_init(argv
[0], VSTREAM_ERR
);
1653 msg_syslog_init(argv
[0], LOG_PID
, LOG_FACILITY
);
1655 if ((config_dir
= getenv(CONF_ENV_PATH
)) != 0
1656 && strcmp(config_dir
, DEF_CONFIG_DIR
) != 0)
1657 msg_fatal("Non-default configuration directory: %s=%s",
1658 CONF_ENV_PATH
, config_dir
);
1663 while ((ch
= GETOPT(argc
, argv
, "ae:g:i:G:I:lpRvx")) > 0) {
1669 if (selection
.type
!= INST_SEL_ALL
)
1670 instance_select_count
++;
1671 selection
.type
= INST_SEL_ALL
;
1674 if ((code
= EDIT_CMD_CODE(optarg
)) < 0)
1675 msg_fatal("Invalid '-e' edit action '%s'. Specify '%s', "
1676 "'%s', '%s', '%s', '%s', '%s', '%s', '%s' or '%s'",
1678 EDIT_CMD_STR(EDIT_CMD_CREATE
),
1679 EDIT_CMD_STR(EDIT_CMD_DESTROY
),
1680 EDIT_CMD_STR(EDIT_CMD_IMPORT
),
1681 EDIT_CMD_STR(EDIT_CMD_DEPORT
),
1682 EDIT_CMD_STR(EDIT_CMD_ENABLE
),
1683 EDIT_CMD_STR(EDIT_CMD_DISABLE
),
1684 EDIT_CMD_STR(EDIT_CMD_ASSIGN
),
1685 EDIT_CMD_STR(EDIT_CMD_INIT
),
1687 if (cmd_mode
!= code
)
1688 command_mode_count
++;
1692 instance_select_count
++;
1693 selection
.type
= INST_SEL_GROUP
;
1694 selection
.name
= optarg
;
1697 instance_select_count
++;
1698 selection
.type
= INST_SEL_NAME
;
1699 selection
.name
= optarg
;
1702 if (assignment
.gname
!= 0)
1703 msg_fatal("Specify at most one '-G' option");
1704 assignment
.gname
= strcmp(optarg
, "-") == 0 ? "" : optarg
;
1707 if (assignment
.name
!= 0)
1708 msg_fatal("Specify at most one '-I' option");
1709 assignment
.name
= strcmp(optarg
, "-") == 0 ? "" : optarg
;
1712 if (cmd_mode
!= ITER_CMD_LIST
)
1713 command_mode_count
++;
1714 cmd_mode
= ITER_CMD_LIST
;
1717 if (cmd_mode
!= ITER_CMD_POSTFIX
)
1718 command_mode_count
++;
1719 cmd_mode
= ITER_CMD_POSTFIX
;
1722 iter_flags
^= ITER_FLAG_REVERSE
;
1726 check_setenv(CONF_ENV_VERB
, "");
1729 if (cmd_mode
!= ITER_CMD_GENERIC
)
1730 command_mode_count
++;
1731 cmd_mode
= ITER_CMD_GENERIC
;
1737 * Report missing arguments, or wrong arguments in the wrong context.
1739 if (instance_select_count
> 1)
1740 msg_fatal("Specity no more than one of '-a', '-g', '-i'");
1742 if (command_mode_count
!= 1)
1743 msg_fatal("Specify exactly one of '-e', '-l', '-p', '-x'");
1745 if (cmd_mode
== ITER_CMD_LIST
&& argc
> optind
)
1746 msg_fatal("Command not allowed with '-l'");
1748 if (cmd_mode
== ITER_CMD_POSTFIX
|| cmd_mode
== ITER_CMD_GENERIC
)
1750 msg_fatal("Command required with '-p' or '-x' option");
1752 if (cmd_mode
== ITER_CMD_POSTFIX
|| (cmd_mode
& EDIT_CMD_MASK_ALL
))
1753 if (iter_flags
!= ITER_FLAG_DEFAULT
)
1754 msg_fatal("The '-p' and '-e' options preclude the use of '-R'");
1756 if ((cmd_mode
& EDIT_CMD_MASK_ASSIGN
) == 0
1757 && (assignment
.name
|| assignment
.gname
)) {
1758 if ((cmd_mode
& EDIT_CMD_MASK_ALL
) == 0)
1759 msg_fatal("Cannot assign instance name or group without '-e %s'",
1760 EDIT_CMD_STR(EDIT_CMD_ASSIGN
));
1762 msg_fatal("Cannot assign instance name or group with '-e %s'",
1763 EDIT_CMD_STR(cmd_mode
));
1765 if (cmd_mode
& EDIT_CMD_MASK_ALL
) {
1766 if (cmd_mode
== EDIT_CMD_ASSIGN
1767 && (assignment
.name
== 0 && assignment
.gname
== 0))
1768 msg_fatal("Specify new instance name or group with '-e %s'",
1769 EDIT_CMD_STR(cmd_mode
));
1771 if ((cmd_mode
& ~EDIT_CMD_MASK_ADD
) != 0 && argc
> optind
)
1772 msg_fatal("Parameter overrides not valid with '-e %s'",
1773 EDIT_CMD_STR(cmd_mode
));
1777 * Proces main.cf parameters.
1780 get_mail_conf_str_table(str_table
);
1785 check_shared_dir_status();
1788 * Iterate over selected instances, or manipulate one instance.
1790 if (cmd_mode
& ITER_CMD_MASK_ALL
)
1791 iterate(cmd_mode
, iter_flags
, argc
- optind
, argv
+ optind
, &selection
);
1793 manage(cmd_mode
, argc
- optind
, argv
+ optind
, &selection
, &assignment
);