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