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