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/* Use "\fBpostconf readme_directory\fR" or "\fBpostconf 364/* html_directory\fR" to locate this information. 365/* MULTI_INSTANCE_README, Postfix multi-instance management 366/* HISTORY 367/* .ad 368/* .fi 369/* The \fBpostmulti\fR(1) command was introduced with Postfix 370/* version 2.6. 371/* LICENSE 372/* .ad 373/* .fi 374/* The Secure Mailer license must be distributed with this software. 375/* AUTHOR(S) 376/* Victor Duchovni 377/* Morgan Stanley 378/* 379/* Wietse Venema 380/* IBM T.J. Watson Research 381/* P.O. Box 704 382/* Yorktown Heights, NY 10598, USA 383/*--*/ 384 385/* System library. */ 386 387#include <sys_defs.h> 388#include <sys/stat.h> 389#include <sys/wait.h> 390#include <vstream.h> 391#include <stdlib.h> 392#include <unistd.h> 393#include <string.h> 394#include <fcntl.h> 395#include <syslog.h> 396#include <errno.h> 397#include <ctype.h> 398#ifdef USE_PATHS_H 399#include <paths.h> 400#endif 401#include <stddef.h> 402 403/* Utility library. */ 404 405#include <msg.h> 406#include <msg_vstream.h> 407#include <msg_syslog.h> 408#include <vstream.h> 409#include <vstring_vstream.h> 410#include <stringops.h> 411#include <clean_env.h> 412#include <argv.h> 413#include <safe.h> 414#include <mymalloc.h> 415#include <htable.h> 416#include <name_code.h> 417#include <ring.h> 418#include <warn_stat.h> 419 420/* Global library. */ 421 422#include <mail_version.h> 423#include <mail_params.h> 424#include <mail_conf.h> 425 426/* Application-specific. */ 427 428 /* 429 * Configuration parameters, specific to postmulti(1). 430 */ 431char *var_multi_start_cmds; 432char *var_multi_stop_cmds; 433char *var_multi_cntrl_cmds; 434 435 /* 436 * Shared directory pathnames. 437 */ 438typedef struct { 439 const char *param_name; 440 char **param_value; 441} SHARED_PATH; 442 443static SHARED_PATH shared_dir_table[] = { 444 VAR_COMMAND_DIR, &var_command_dir, 445 VAR_DAEMON_DIR, &var_daemon_dir, 446 0, 447}; 448 449 /* 450 * Actions. 451 */ 452#define ITER_CMD_POSTFIX (1<<0) /* postfix(1) iterator mode */ 453#define ITER_CMD_LIST (1<<1) /* listing iterator mode */ 454#define ITER_CMD_GENERIC (1<<2) /* generic command iterator mode */ 455 456#define ITER_CMD_MASK_ALL \ 457 (ITER_CMD_POSTFIX | ITER_CMD_LIST | ITER_CMD_GENERIC) 458 459#define EDIT_CMD_CREATE (1<<4) /* create new instance */ 460#define EDIT_CMD_IMPORT (1<<5) /* import existing instance */ 461#define EDIT_CMD_DESTROY (1<<6) /* destroy instance */ 462#define EDIT_CMD_DEPORT (1<<7) /* export instance */ 463#define EDIT_CMD_ENABLE (1<<8) /* enable start/stop */ 464#define EDIT_CMD_DISABLE (1<<9) /* disable start/stop */ 465#define EDIT_CMD_ASSIGN (1<<10) /* assign name/group */ 466#define EDIT_CMD_INIT (1<<11) /* hook into main.cf */ 467 468#define EDIT_CMD_MASK_ADD (EDIT_CMD_CREATE | EDIT_CMD_IMPORT) 469#define EDIT_CMD_MASK_DEL (EDIT_CMD_DESTROY | EDIT_CMD_DEPORT) 470#define EDIT_CMD_MASK_ASSIGN (EDIT_CMD_MASK_ADD | EDIT_CMD_ASSIGN) 471#define EDIT_CMD_MASK_ENB (EDIT_CMD_ENABLE | EDIT_CMD_DISABLE) 472#define EDIT_CMD_MASK_ALL \ 473 (EDIT_CMD_MASK_ASSIGN | EDIT_CMD_MASK_DEL | EDIT_CMD_MASK_ENB | \ 474 EDIT_CMD_INIT) 475 476 /* 477 * Edit command to number mapping, and vice versa. 478 */ 479static NAME_CODE edit_command_table[] = { 480 "create", EDIT_CMD_CREATE, 481 "import", EDIT_CMD_IMPORT, 482 "destroy", EDIT_CMD_DESTROY, 483 "deport", EDIT_CMD_DEPORT, 484 "enable", EDIT_CMD_ENABLE, 485 "disable", EDIT_CMD_DISABLE, 486 "assign", EDIT_CMD_ASSIGN, 487 "init", EDIT_CMD_INIT, 488 0, -1, 489}; 490 491#define EDIT_CMD_CODE(str) \ 492 name_code(edit_command_table, NAME_CODE_FLAG_STRICT_CASE, (str)) 493#define EDIT_CMD_STR(code) str_name_code(edit_command_table, (code)) 494 495 /* 496 * Mandatory prefix for non-empty instance names. 497 */ 498#ifndef NAME_PREFIX 499#define NAME_PREFIX "postfix-" 500#endif 501#define HAS_NAME_PREFIX(name) \ 502 (strncmp((name), NAME_PREFIX, sizeof(NAME_PREFIX)-1) == 0) 503#define NEED_NAME_PREFIX(name) \ 504 ((name) != 0 && strcmp((name), "-") != 0 && !HAS_NAME_PREFIX(name)) 505#define NAME_SUFFIX(name) ((name) + sizeof(NAME_PREFIX) - 1) 506 507 /* 508 * In-core instance structure. Only private information is kept here. 509 */ 510typedef struct instance { 511 RING ring; /* linkage. */ 512 char *config_dir; /* private */ 513 char *queue_dir; /* private */ 514 char *data_dir; /* private */ 515 char *name; /* null or name */ 516 char *gname; /* null or group */ 517 int enabled; /* start/stop enable */ 518 int primary; /* special */ 519} INSTANCE; 520 521 /* 522 * Managed instance list (edit mode and iterator mode). 523 */ 524static RING instance_hd[1]; /* instance list head */ 525 526#define RING_TO_INSTANCE(ring_ptr) RING_TO_APPL(ring_ptr, INSTANCE, ring) 527#define RING_PTR_OF(x) (&((x)->ring)) 528 529#define FOREACH_INSTANCE(entry) \ 530 for ((entry) = instance_hd; \ 531 ((entry) = ring_succ(entry)) != instance_hd;) 532 533#define FOREACH_SECONDARY_INSTANCE(entry) \ 534 for ((entry) = ring_succ(instance_hd); \ 535 ((entry) = ring_succ(entry)) != instance_hd;) 536 537#define NEXT_ITERATOR_INSTANCE(flags, entry) \ 538 (((flags) & ITER_FLAG_REVERSE) ? ring_pred(entry) : ring_succ(entry)) 539 540#define FOREACH_ITERATOR_INSTANCE(flags, entry) \ 541 for ((entry) = instance_hd; \ 542 ((entry) = NEXT_ITERATOR_INSTANCE(flags, (entry))) != instance_hd;) 543 544 /* 545 * Instance selection. One can either select all instances, select by 546 * instance name, or select by instance group. 547 */ 548typedef struct { 549 int type; /* see below */ 550 char *name; /* undefined or name */ 551} INST_SELECTION; 552 553#define INST_SEL_NONE 0 /* default: no selection */ 554#define INST_SEL_ALL 1 /* select all instances */ 555#define INST_SEL_NAME 2 /* select instance name */ 556#define INST_SEL_GROUP 3 /* select instance group */ 557 558 /* 559 * Instance name assignment. Each instance may be assigned an instance name 560 * (this must be globally unique within a multi-instance cluster) or an 561 * instance group name (this is intended to be shared). Externally, empty 562 * names may be represented as "-". Internally, we use "" only, to simplify 563 * the code. 564 */ 565typedef struct { 566 char *name; /* null or assigned instance name */ 567 char *gname; /* null or assigned group name */ 568} NAME_ASSIGNMENT; 569 570 /* 571 * Iterator controls for non-edit commands. One can reverse the iteration 572 * order, or give special treatment to disabled instances. 573 */ 574#define ITER_FLAG_DEFAULT 0 /* default setting */ 575#define ITER_FLAG_REVERSE (1<<0) /* reverse iteration order */ 576#define ITER_FLAG_CHECK_DISABLED (1<<1) /* check disabled instances */ 577#define ITER_FLAG_SKIP_DISABLED (1<<2) /* skip disabled instances */ 578 579 /* 580 * Environment export controls for edit commands. postmulti(1) exports only 581 * things that need to be updated. 582 */ 583#define EXP_FLAG_MULTI_DIRS (1<<0) /* export multi_instance_directories */ 584#define EXP_FLAG_MULTI_NAME (1<<1) /* export multi_instance_name */ 585#define EXP_FLAG_MULTI_GROUP (1<<2) /* export multi_instance_group */ 586 587 /* 588 * To detect conflicts, each instance name and each shared or private 589 * pathname is registered in one place, with its owner. Everyone must 590 * register their claims when they join, and will be rejected in case of 591 * conlict. 592 * 593 * Each claim value involves a parameter value (either a directory name or an 594 * instance name). Each claim owner is the config_directory pathname plus 595 * the parameter name. 596 * 597 * XXX: No multi.cf lock file, so this is not race-free. 598 */ 599static HTABLE *claim_table; 600 601#define IS_CLAIMED_BY(name) \ 602 (claim_table ? htable_find(claim_table, (name)) : 0) 603 604 /* 605 * Forward references. 606 */ 607static int iterate_command(int, int, char **, INST_SELECTION *); 608static int match_instance_selection(INSTANCE *, INST_SELECTION *); 609 610 /* 611 * Convenience. 612 */ 613#define INSTANCE_NAME(i) ((i)->name ? (i)->name : (i)->config_dir) 614#define STR(buf) vstring_str(buf) 615 616/* register_claim - register claim or bust */ 617 618static void register_claim(const char *instance_path, const char *param_name, 619 const char *param_value) 620{ 621 const char *myname = "register_claim"; 622 char *requestor; 623 const char *owner; 624 625 /* 626 * Sanity checks. 627 */ 628 if (instance_path == 0 || *instance_path == 0) 629 msg_panic("%s: no or empty instance pathname", myname); 630 if (param_name == 0 || *param_name == 0) 631 msg_panic("%s: no or empty parameter name", myname); 632 if (param_value == 0) 633 msg_panic("%s: no parameter value", myname); 634 635 /* 636 * Make a claim or report a conflict. 637 */ 638 if (claim_table == 0) 639 claim_table = htable_create(100); 640 requestor = concatenate(instance_path, ", ", param_name, (char *) 0); 641 if ((owner = htable_find(claim_table, param_value)) == 0) { 642 (void) htable_enter(claim_table, param_value, requestor); 643 } else if (strcmp(owner, requestor) == 0) { 644 myfree(requestor); 645 } else { 646 msg_fatal("instance %s, %s=%s conflicts with instance %s=%s", 647 instance_path, param_name, param_value, owner, param_value); 648 } 649} 650 651/* claim_instance_attributes - claim multiple private instance attributes */ 652 653static void claim_instance_attributes(INSTANCE *ip) 654{ 655 656 /* 657 * Detect instance name or pathname conflicts between this instance and 658 * other instances. XXX: No multi.cf lock file, so this is not race-free. 659 */ 660 if (ip->name) 661 register_claim(ip->config_dir, VAR_MULTI_NAME, ip->name); 662 register_claim(ip->config_dir, VAR_CONFIG_DIR, ip->config_dir); 663 register_claim(ip->config_dir, VAR_QUEUE_DIR, ip->queue_dir); 664 register_claim(ip->config_dir, VAR_DATA_DIR, ip->data_dir); 665} 666 667/* alloc_instance - allocate a single instance object */ 668 669static INSTANCE *alloc_instance(const char *config_dir) 670{ 671 INSTANCE *ip = (INSTANCE *) mymalloc(sizeof(INSTANCE)); 672 673 ring_init(RING_PTR_OF(ip)); 674 ip->config_dir = config_dir ? mystrdup(config_dir) : 0; 675 ip->queue_dir = 0; 676 ip->data_dir = 0; 677 ip->name = 0; 678 ip->gname = 0; 679 ip->enabled = 0; 680 ip->primary = 0; 681 682 return (ip); 683} 684 685#if 0 686 687/* free_instance - free a single instance object */ 688 689static void free_instance(INSTANCE *ip) 690{ 691 692 /* 693 * If we continue after secondary main.cf file read error, we must be 694 * prepared for the case that some parameters may be missing. 695 */ 696 if (ip->name) 697 myfree(ip->name); 698 if (ip->gname) 699 myfree(ip->gname); 700 if (ip->config_dir) 701 myfree(ip->config_dir); 702 if (ip->queue_dir) 703 myfree(ip->queue_dir); 704 if (ip->data_dir) 705 myfree(ip->data_dir); 706 myfree((char *) ip); 707} 708 709#endif 710 711/* insert_instance - insert instance before selected location, claim names */ 712 713static void insert_instance(INSTANCE *ip, INST_SELECTION *selection) 714{ 715 RING *old; 716 717#define append_instance(ip) insert_instance((ip), (INST_SELECTION *) 0) 718 719 /* 720 * Insert instance before the selected site. 721 */ 722 claim_instance_attributes(ip); 723 if (ring_succ(instance_hd) == 0) 724 ring_init(instance_hd); 725 if (selection && selection->type != INST_SEL_NONE) { 726 FOREACH_SECONDARY_INSTANCE(old) { 727 if (match_instance_selection(RING_TO_INSTANCE(old), selection)) { 728 ring_prepend(old, RING_PTR_OF(ip)); 729 return; 730 } 731 } 732 if (selection->type != INST_SEL_ALL) 733 msg_fatal("No matching secondary instances"); 734 } 735 ring_prepend(instance_hd, RING_PTR_OF(ip)); 736} 737 738/* create_primary_instance - synthetic entry for primary instance */ 739 740static INSTANCE *create_primary_instance(void) 741{ 742 INSTANCE *ip = alloc_instance(var_config_dir); 743 744 /* 745 * There is no need to load primary instance paramater settings from 746 * file. We already have the main.cf parameters of interest in memory. 747 */ 748#define SAVE_INSTANCE_NAME(val) (*(val) ? mystrdup(val) : 0) 749 750 ip->name = SAVE_INSTANCE_NAME(var_multi_name); 751 ip->gname = SAVE_INSTANCE_NAME(var_multi_group); 752 ip->enabled = var_multi_enable; 753 ip->queue_dir = mystrdup(var_queue_dir); 754 ip->data_dir = mystrdup(var_data_dir); 755 ip->primary = 1; 756 return (ip); 757} 758 759/* load_instance - read instance parameters from config_dir/main.cf */ 760 761static INSTANCE *load_instance(INSTANCE *ip) 762{ 763 VSTREAM *pipe; 764 VSTRING *buf; 765 char *name; 766 char *value; 767 ARGV *cmd; 768 int count = 0; 769 static NAME_CODE bool_code[] = { 770 CONFIG_BOOL_YES, 1, 771 CONFIG_BOOL_NO, 0, 772 0, -1, 773 }; 774 775 /* 776 * XXX: We could really use a "postconf -E" to expand values in the 777 * context of the target main.cf! 778 */ 779#define REQUEST_PARAM_COUNT 5 /* # of requested parameters */ 780 781 cmd = argv_alloc(REQUEST_PARAM_COUNT + 3); 782 name = concatenate(var_command_dir, "/", "postconf", (char *) 0); 783 argv_add(cmd, name, "-c", ip->config_dir, 784 VAR_QUEUE_DIR, VAR_DATA_DIR, 785 VAR_MULTI_NAME, VAR_MULTI_GROUP, VAR_MULTI_ENABLE, 786 (char *) 0); 787 myfree(name); 788 pipe = vstream_popen(O_RDONLY, VSTREAM_POPEN_ARGV, cmd->argv, 789 VSTREAM_POPEN_END); 790 argv_free(cmd); 791 if (pipe == 0) 792 msg_fatal("Cannot parse %s/main.cf file: %m", ip->config_dir); 793 794 /* 795 * Read parameter settings from postconf. See also comments below on 796 * whether we should continue or skip groups after error instead of 797 * bailing out immediately. 798 */ 799 buf = vstring_alloc(100); 800 while (vstring_get_nonl(buf, pipe) != VSTREAM_EOF) { 801 if (split_nameval(STR(buf), &name, &value)) 802 msg_fatal("Invalid %s/main.cf parameter: %s", 803 ip->config_dir, STR(buf)); 804 if (strcmp(name, VAR_QUEUE_DIR) == 0 && ++count) 805 ip->queue_dir = mystrdup(value); 806 else if (strcmp(name, VAR_DATA_DIR) == 0 && ++count) 807 ip->data_dir = mystrdup(value); 808 else if (strcmp(name, VAR_MULTI_NAME) == 0 && ++count) 809 ip->name = SAVE_INSTANCE_NAME(value); 810 else if (strcmp(name, VAR_MULTI_GROUP) == 0 && ++count) 811 ip->gname = SAVE_INSTANCE_NAME(value); 812 else if (strcmp(name, VAR_MULTI_ENABLE) == 0 && ++count) { 813 /* mail_conf_bool(3) is case insensitive! */ 814 ip->enabled = name_code(bool_code, NAME_CODE_FLAG_NONE, value); 815 if (ip->enabled < 0) 816 msg_fatal("Unexpected %s/main.cf entry: %s = %s", 817 ip->config_dir, VAR_MULTI_ENABLE, value); 818 } 819 } 820 vstring_free(buf); 821 822 /* 823 * XXX We should not bail out while reading a bad secondary main.cf file. 824 * When we manage dozens or more instances, the likelihood increases that 825 * some file will be damaged or missing after a system crash. That is not 826 * a good reason to prevent undamaged Postfix instances from starting. 827 */ 828 if (count != REQUEST_PARAM_COUNT) 829 msg_fatal("Failed to obtain all required %s/main.cf parameters", 830 ip->config_dir); 831 832 if (vstream_pclose(pipe)) 833 msg_fatal("Cannot parse %s/main.cf file", ip->config_dir); 834 return (ip); 835} 836 837/* load_all_instances - compute list of Postfix instances */ 838 839static void load_all_instances(void) 840{ 841 INSTANCE *primary_instance; 842 char **cpp; 843 ARGV *secondary_names; 844 845 /* 846 * Avoid unexpected behavior when $multi_instance_directories contains 847 * only comma characters. Count the actual number of elements, before we 848 * decide that the list is empty. 849 */ 850 secondary_names = argv_split(var_multi_conf_dirs, "\t\n\r, "); 851 852 /* 853 * First, the primary instance. This is synthesized out of thin air. 854 */ 855 primary_instance = create_primary_instance(); 856 if (secondary_names->argc == 0) 857 primary_instance->enabled = 1; /* Single-instance mode */ 858 append_instance(primary_instance); 859 860 /* 861 * Next, instances defined in $multi_instance_directories. Note: 862 * load_instance() has side effects on the global config dictionary, but 863 * this does not affect the values that have already been extracted into 864 * C variables. 865 */ 866 for (cpp = secondary_names->argv; *cpp != 0; cpp++) 867 append_instance(load_instance(alloc_instance(*cpp))); 868 869 argv_free(secondary_names); 870} 871 872/* match_instance_selection - match all/name/group constraints */ 873 874static int match_instance_selection(INSTANCE *ip, INST_SELECTION *selection) 875{ 876 char *iname; 877 char *name; 878 879 /* 880 * When selecting (rather than assigning names) an instance, we match by 881 * the instance name, config_directory path, or the instance name suffix 882 * (name without mandatory prefix). Selecting "-" selects the primary 883 * instance. 884 */ 885 switch (selection->type) { 886 case INST_SEL_NONE: 887 return (0); 888 case INST_SEL_ALL: 889 return (1); 890 case INST_SEL_GROUP: 891 return (ip->gname != 0 && strcmp(selection->name, ip->gname) == 0); 892 case INST_SEL_NAME: 893 name = selection->name; 894 if (*name == '/' || ip->name == 0) 895 iname = ip->config_dir; 896 else if (!HAS_NAME_PREFIX(name) && HAS_NAME_PREFIX(ip->name)) 897 iname = NAME_SUFFIX(ip->name); 898 else 899 iname = ip->name; 900 return (strcmp(name, iname) == 0 901 || (ip->primary && strcmp(name, "-") == 0)); 902 default: 903 msg_panic("match_instance_selection: unknown selection type: %d", 904 selection->type); 905 } 906} 907 908/* check_setenv - setenv() with extreme prejudice */ 909 910static void check_setenv(const char *name, const char *value) 911{ 912#define CLOBBER 1 913 if (setenv(name, value, CLOBBER) < 0) 914 msg_fatal("setenv: %m"); 915} 916 917/* prepend_command_path - prepend command_directory to PATH */ 918 919static void prepend_command_path(void) 920{ 921 char *cmd_path; 922 923 /* 924 * Carefully prepend "$command_directory:" to PATH. We can free the 925 * buffer after check_setenv(), since the value is copied there. 926 */ 927 cmd_path = safe_getenv("PATH"); 928 cmd_path = concatenate(var_command_dir, ":", (cmd_path && *cmd_path) ? 929 cmd_path : ROOT_PATH, (char *) 0); 930 check_setenv("PATH", cmd_path); 931 myfree(cmd_path); 932} 933 934/* check_shared_dir_status - check and claim shared directories */ 935 936static void check_shared_dir_status(void) 937{ 938 struct stat st; 939 const SHARED_PATH *sp; 940 941 for (sp = shared_dir_table; sp->param_name; ++sp) { 942 if (stat(sp->param_value[0], &st) < 0) 943 msg_fatal("%s = '%s': directory not found: %m", 944 sp->param_name, sp->param_value[0]); 945 if (!S_ISDIR(st.st_mode)) 946 msg_fatal("%s = '%s' is not a directory", 947 sp->param_name, sp->param_value[0]); 948 register_claim(var_config_dir, sp->param_name, sp->param_value[0]); 949 } 950} 951 952/* check_safe_name - allow instance or group name with only "safe" characters */ 953 954static int check_safe_name(const char *s) 955{ 956#define SAFE_PUNCT "!@%-_=+:./" 957 if (*s == 0) 958 return (0); 959 for (; *s; ++s) { 960 if (!ISALNUM(*s) && !strchr(SAFE_PUNCT, *s)) 961 return (0); 962 } 963 return (1); 964} 965 966/* check_name_assignments - Check validity of assigned instance or group name */ 967 968static void check_name_assignments(NAME_ASSIGNMENT *assignment) 969{ 970 971 /* 972 * Syntax check the assigned instance name. This name is also used to 973 * generate directory pathnames, so we must not allow "/" characters. 974 * 975 * The value "" will clear the name and is always valid. The command-line 976 * parser has already converted "-" into "", to simplify implementation. 977 */ 978 if (assignment->name && *assignment->name) { 979 if (!check_safe_name(assignment->name)) 980 msg_fatal("Unsafe characters in new instance name: '%s'", 981 assignment->name); 982 if (strchr(assignment->name, '/')) 983 msg_fatal("Illegal '/' character in new instance name: '%s'", 984 assignment->name); 985 if (NEED_NAME_PREFIX(assignment->name)) 986 msg_fatal("New instance name must start with '%s'", 987 NAME_PREFIX); 988 } 989 990 /* 991 * Syntax check the assigned group name. 992 */ 993 if (assignment->gname && *assignment->gname) { 994 if (!check_safe_name(assignment->gname)) 995 msg_fatal("Unsafe characters in '-G %s'", assignment->gname); 996 } 997} 998 999/* do_name_assignments - assign instance/group names */ 1000 1001static int do_name_assignments(INSTANCE *target, NAME_ASSIGNMENT *assignment) 1002{ 1003 int export_flags = 0; 1004 1005 /* 1006 * The command-line parser has already converted "-" into "", to simplify 1007 * implementation. 1008 */ 1009 if (assignment->name 1010 && strcmp(assignment->name, target->name ? target->name : "")) { 1011 register_claim(target->config_dir, VAR_MULTI_NAME, assignment->name); 1012 if (target->name) 1013 myfree(target->name); 1014 target->name = SAVE_INSTANCE_NAME(assignment->name); 1015 export_flags |= EXP_FLAG_MULTI_NAME; 1016 } 1017 if (assignment->gname 1018 && strcmp(assignment->gname, target->gname ? target->gname : "")) { 1019 if (target->gname) 1020 myfree(target->gname); 1021 target->gname = SAVE_INSTANCE_NAME(assignment->gname); 1022 export_flags |= EXP_FLAG_MULTI_GROUP; 1023 } 1024 return (export_flags); 1025} 1026 1027/* make_private_path - generate secondary pathname using primary as template */ 1028 1029static char *make_private_path(const char *param_name, 1030 const char *primary_value, 1031 NAME_ASSIGNMENT *assignment) 1032{ 1033 char *path; 1034 char *base; 1035 char *end; 1036 1037 /* 1038 * The command-line parser has already converted "-" into "", to simplify 1039 * implementation. 1040 */ 1041 if (assignment->name == 0 || *assignment->name == 0) 1042 msg_fatal("Missing %s parameter value", param_name); 1043 1044 if (*primary_value != '/') 1045 msg_fatal("Invalid default %s parameter value: '%s': " 1046 "specify an absolute pathname", 1047 param_name, primary_value); 1048 1049 base = mystrdup(primary_value); 1050 if ((end = strrchr(base, '/')) != 0) { 1051 /* Drop trailing slashes */ 1052 if (end[1] == '\0') { 1053 while (--end > base && *end == '/') 1054 *end = '\0'; 1055 end = strrchr(base, '/'); 1056 } 1057 /* Drop last path component */ 1058 while (end > base && *end == '/') 1059 *end-- = '\0'; 1060 } 1061 path = concatenate(base[1] ? base : "", "/", 1062 assignment->name, (char *) 0); 1063 myfree(base); 1064 return (path); 1065} 1066 1067/* assign_new_parameter - assign new instance private name=value */ 1068 1069static void assign_new_parameter(INSTANCE *new, int edit_cmd, 1070 const char *arg) 1071{ 1072 char *saved_arg; 1073 char *name; 1074 char *value; 1075 char *end; 1076 char **target = 0; 1077 1078 /* 1079 * With "import", only config_directory is specified on the command line 1080 * (either explicitly as config_directory=/path/name, or implicitly as 1081 * instance name). The other private directory pathnames are taken from 1082 * the existing instance's main.cf file. 1083 * 1084 * With "create", all private pathname parameters are specified on the 1085 * command line, or generated from an instance name. 1086 */ 1087 saved_arg = mystrdup(arg); 1088 if (split_nameval(saved_arg, &name, &value)) 1089 msg_fatal("Malformed parameter setting '%s'", arg); 1090 1091 if (strcmp(VAR_CONFIG_DIR, name) == 0) { 1092 target = &new->config_dir; 1093 } else if (edit_cmd != EDIT_CMD_IMPORT) { 1094 if (strcmp(VAR_QUEUE_DIR, name) == 0) { 1095 target = &new->queue_dir; 1096 } else if (strcmp(VAR_DATA_DIR, name) == 0) { 1097 target = &new->data_dir; 1098 } 1099 } 1100 if (target == 0) 1101 msg_fatal("Parameter '%s' not valid with action %s", 1102 name, EDIT_CMD_STR(edit_cmd)); 1103 1104 /* 1105 * Extract and assign the parameter value. We do a limited number of 1106 * checks here. Conflicts between instances are checked by the caller. 1107 * More checks may be implemented in the helper script if inspired. 1108 */ 1109 if (*value != '/') 1110 msg_fatal("Parameter setting '%s' is not an absolute path", name); 1111 1112 /* Tolerate+trim trailing "/" from readline completion */ 1113 for (end = value + strlen(value) - 1; end > value && *end == '/'; --end) 1114 *end = 0; 1115 1116 /* No checks here for "/." or other shoot-foot silliness. */ 1117 if (end == value) 1118 msg_fatal("Parameter setting '%s' is the root directory", name); 1119 1120 if (*target) 1121 myfree(*target); 1122 *target = mystrdup(value); 1123 1124 /* 1125 * Cleanup. 1126 */ 1127 myfree(saved_arg); 1128} 1129 1130/* assign_new_parameters - initialize new instance private parameters */ 1131 1132static void assign_new_parameters(INSTANCE *new, int edit_cmd, 1133 char **argv, NAME_ASSIGNMENT *assignment) 1134{ 1135 const char *owner; 1136 1137 /* 1138 * Sanity check the explicit parameter settings. More stringent checks 1139 * may take place in the helper script. 1140 */ 1141 while (*argv) 1142 assign_new_parameter(new, edit_cmd, *argv++); 1143 1144 /* 1145 * Initialize any missing private directory pathnames, using the primary 1146 * configuration directory parameter values as a template, and using the 1147 * assigned instance name to fill in the blanks. 1148 * 1149 * When importing an existing instance, load private directory pathnames 1150 * from its main.cf file. 1151 */ 1152 if (new->config_dir == 0) 1153 new->config_dir = 1154 make_private_path(VAR_CONFIG_DIR, var_config_dir, assignment); 1155 /* Needed for better-quality error message. */ 1156 if ((owner = IS_CLAIMED_BY(new->config_dir)) != 0) 1157 msg_fatal("new %s=%s is already in use by instance %s=%s", 1158 VAR_CONFIG_DIR, new->config_dir, owner, new->config_dir); 1159 if (edit_cmd != EDIT_CMD_IMPORT) { 1160 if (new->queue_dir == 0) 1161 new->queue_dir = 1162 make_private_path(VAR_QUEUE_DIR, var_queue_dir, assignment); 1163 if (new->data_dir == 0) 1164 new->data_dir = 1165 make_private_path(VAR_DATA_DIR, var_data_dir, assignment); 1166 } else { 1167 load_instance(new); 1168 } 1169} 1170 1171/* export_helper_environment - update environment settings for helper command */ 1172 1173static void export_helper_environment(INSTANCE *target, int export_flags) 1174{ 1175 ARGV *import_env; 1176 VSTRING *multi_dirs; 1177 const SHARED_PATH *sp; 1178 RING *entry; 1179 1180 /* 1181 * Environment import filter, to enforce consistent behavior whether this 1182 * command is started by hand, or at system boot time. This is necessary 1183 * because some shell scripts use environment settings to override 1184 * main.cf settings. 1185 */ 1186 import_env = argv_split(var_import_environ, ", \t\r\n"); 1187 clean_env(import_env->argv); 1188 argv_free(import_env); 1189 1190 /* 1191 * Prepend $command_directory: to PATH. This supposedly ensures that 1192 * naive programs will execute commands from the right Postfix version. 1193 */ 1194 prepend_command_path(); 1195 1196 /* 1197 * The following ensures that Postfix's own programs will target the 1198 * primary instance. 1199 */ 1200 check_setenv(CONF_ENV_PATH, var_config_dir); 1201 1202 /* 1203 * Export the parameter settings that are shared between instances. 1204 */ 1205 for (sp = shared_dir_table; sp->param_name; ++sp) 1206 check_setenv(sp->param_name, sp->param_value[0]); 1207 1208 /* 1209 * Export the target instance's private directory locations. 1210 */ 1211 check_setenv(VAR_CONFIG_DIR, target->config_dir); 1212 check_setenv(VAR_QUEUE_DIR, target->queue_dir); 1213 check_setenv(VAR_DATA_DIR, target->data_dir); 1214 1215 /* 1216 * With operations that add or delete a secondary instance, we export the 1217 * modified multi_instance_directories parameter value for the primary 1218 * Postfix instance. 1219 */ 1220 if (export_flags & EXP_FLAG_MULTI_DIRS) { 1221 multi_dirs = vstring_alloc(100); 1222 FOREACH_SECONDARY_INSTANCE(entry) { 1223 if (VSTRING_LEN(multi_dirs) > 0) 1224 VSTRING_ADDCH(multi_dirs, ' '); 1225 vstring_strcat(multi_dirs, RING_TO_INSTANCE(entry)->config_dir); 1226 } 1227 check_setenv(VAR_MULTI_CONF_DIRS, STR(multi_dirs)); 1228 vstring_free(multi_dirs); 1229 } 1230 1231 /* 1232 * Export updates for the instance name and group. Empty value (or no 1233 * export) means don't update, "-" means clear. 1234 */ 1235 if (export_flags & EXP_FLAG_MULTI_NAME) 1236 check_setenv(VAR_MULTI_NAME, target->name && *target->name ? 1237 target->name : "-"); 1238 1239 if (export_flags & EXP_FLAG_MULTI_GROUP) 1240 check_setenv(VAR_MULTI_GROUP, target->gname && *target->gname ? 1241 target->gname : "-"); 1242 1243 /* 1244 * If we would implement enable/disable commands by exporting the updated 1245 * parameter value, then we could skip commands that have no effect, just 1246 * like we can skip "assign" commands that make no change. 1247 */ 1248} 1249 1250/* install_new_instance - install and return newly created instance */ 1251 1252static INSTANCE *install_new_instance(int edit_cmd, char **argv, 1253 INST_SELECTION *selection, 1254 NAME_ASSIGNMENT *assignment, 1255 int *export_flags) 1256{ 1257 INSTANCE *new; 1258 1259 new = alloc_instance((char *) 0); 1260 check_name_assignments(assignment); 1261 assign_new_parameters(new, edit_cmd, argv, assignment); 1262 *export_flags |= 1263 (do_name_assignments(new, assignment) | EXP_FLAG_MULTI_DIRS); 1264 insert_instance(new, selection); 1265 return (new); 1266} 1267 1268/* update_instance - update existing instance, return export flags */ 1269 1270static int update_instance(INSTANCE *target, NAME_ASSIGNMENT *assignment) 1271{ 1272 int export_flags; 1273 1274 check_name_assignments(assignment); 1275 export_flags = do_name_assignments(target, assignment); 1276 return (export_flags); 1277} 1278 1279/* select_existing_instance - return instance selected for management */ 1280 1281static INSTANCE *select_existing_instance(INST_SELECTION *selection, 1282 int unlink_flag, 1283 int *export_flags) 1284{ 1285 INSTANCE *selected = 0; 1286 RING *entry; 1287 INSTANCE *ip; 1288 1289#define DONT_UNLINK 0 1290#define DO_UNLINK 1 1291 1292 if (selection->type != INST_SEL_NAME) 1293 msg_fatal("Select an instance via '-i name'"); 1294 1295 /* Find the selected instance and its predecessor */ 1296 FOREACH_INSTANCE(entry) { 1297 if (match_instance_selection(ip = RING_TO_INSTANCE(entry), selection)) { 1298 selected = ip; 1299 break; 1300 } 1301 } 1302 1303 if (selected == 0) 1304 msg_fatal("No instance named %s", selection->name); 1305 1306 if (unlink_flag) { 1307 /* Splice the target instance out of the list */ 1308 if (ring_pred(entry) == instance_hd) 1309 msg_fatal("Cannot remove the primary instance"); 1310 if (selected->enabled) 1311 msg_fatal("Cannot remove enabled instances"); 1312 ring_detach(entry); 1313 if (export_flags == 0) 1314 msg_panic("select_existing_instance: no export flags"); 1315 *export_flags |= EXP_FLAG_MULTI_DIRS; 1316 } 1317 return (selected); 1318} 1319 1320/* manage - create/destroy/... manage instances */ 1321 1322static NORETURN manage(int edit_cmd, int argc, char **argv, 1323 INST_SELECTION *selection, 1324 NAME_ASSIGNMENT *assignment) 1325{ 1326 char *cmd; 1327 INSTANCE *target; 1328 int export_flags; 1329 1330 /* 1331 * Edit mode is not subject to iterator controls. 1332 */ 1333#define NO_EXPORT_FLAGS ((int *) 0) 1334 export_flags = 0; 1335 1336 switch (edit_cmd) { 1337 case EDIT_CMD_INIT: 1338 target = create_primary_instance(); 1339 break; 1340 1341 case EDIT_CMD_CREATE: 1342 case EDIT_CMD_IMPORT: 1343 load_all_instances(); 1344 target = install_new_instance(edit_cmd, argv, selection, 1345 assignment, &export_flags); 1346 break; 1347 1348 case EDIT_CMD_ASSIGN: 1349 load_all_instances(); 1350 target = 1351 select_existing_instance(selection, DONT_UNLINK, NO_EXPORT_FLAGS); 1352 export_flags |= update_instance(target, assignment); 1353 if (export_flags == 0) 1354 exit(0); 1355 break; 1356 1357 case EDIT_CMD_DESTROY: 1358 case EDIT_CMD_DEPORT: 1359 load_all_instances(); 1360 target = select_existing_instance(selection, DO_UNLINK, &export_flags); 1361 break; 1362 1363 default: 1364 load_all_instances(); 1365 target = 1366 select_existing_instance(selection, DONT_UNLINK, NO_EXPORT_FLAGS); 1367 break; 1368 } 1369 1370 /* 1371 * Set up the helper script's process environment, and execute the helper 1372 * script. 1373 */ 1374#define HELPER "postmulti-script" 1375 1376 export_helper_environment(target, export_flags); 1377 cmd = concatenate(var_daemon_dir, "/" HELPER, (char *) 0); 1378 execl(cmd, cmd, "-e", EDIT_CMD_STR(edit_cmd), (char *) 0); 1379 msg_fatal("%s: %m", cmd); 1380} 1381 1382/* run_user_command - execute external command with requested MAIL_CONFIG env */ 1383 1384static int run_user_command(INSTANCE *ip, int iter_cmd, int iter_flags, 1385 char **argv) 1386{ 1387 WAIT_STATUS_T status; 1388 int pid; 1389 int wpid; 1390 1391 /* 1392 * Set up a process environment. The postfix(1) command needs MAIL_CONFIG 1393 * (or the equivalent command-line option); it overrides everything else. 1394 * 1395 * postmulti(1) typically runs various Postfix utilities (postsuper, ...) in 1396 * the context of one or more instances. It can also run various scripts 1397 * on the users PATH. So we can't clobber the user's PATH, but do want to 1398 * make sure that the utilities in $command_directory are always found in 1399 * the right place (or at all). 1400 */ 1401 switch (pid = fork()) { 1402 case -1: 1403 msg_warn("fork %s: %m", argv[0]); 1404 return -1; 1405 case 0: 1406 check_setenv(CONF_ENV_PATH, ip->config_dir); 1407 if (iter_cmd != ITER_CMD_POSTFIX) { 1408 check_setenv(VAR_DAEMON_DIR, var_daemon_dir); 1409 check_setenv(VAR_COMMAND_DIR, var_command_dir); 1410 check_setenv(VAR_CONFIG_DIR, ip->config_dir); 1411 check_setenv(VAR_QUEUE_DIR, ip->queue_dir); 1412 check_setenv(VAR_DATA_DIR, ip->data_dir); 1413 check_setenv(VAR_MULTI_NAME, ip->name ? ip->name : ""); 1414 check_setenv(VAR_MULTI_GROUP, ip->gname ? ip->gname : ""); 1415 check_setenv(VAR_MULTI_ENABLE, ip->enabled ? 1416 CONFIG_BOOL_YES : CONFIG_BOOL_NO); 1417 prepend_command_path(); 1418 } 1419 1420 /* 1421 * Replace: postfix -- start ... With: postfix -- check ... 1422 */ 1423 if (iter_cmd == ITER_CMD_POSTFIX 1424 && (iter_flags & ITER_FLAG_CHECK_DISABLED) && !ip->enabled) 1425 argv[2] = "check"; 1426 1427 execvp(argv[0], argv); 1428 msg_fatal("execvp %s: %m", argv[0]); 1429 default: 1430 do { 1431 wpid = waitpid(pid, &status, 0); 1432 } while (wpid == -1 && errno == EINTR); 1433 return (wpid == -1 ? -1 : 1434 WIFEXITED(status) ? WEXITSTATUS(status) : 1); 1435 } 1436} 1437 1438/* word_in_list - look up command in start, stop, or control list */ 1439 1440static int word_in_list(char *cmdlist, const char *cmd) 1441{ 1442 char *saved; 1443 char *cp; 1444 char *elem; 1445 1446 cp = saved = mystrdup(cmdlist); 1447 while ((elem = mystrtok(&cp, "\t\n\r, ")) != 0 && strcmp(elem, cmd) != 0) 1448 /* void */ ; 1449 myfree(saved); 1450 return (elem != 0); 1451} 1452 1453/* iterate_postfix_command - execute postfix(1) command */ 1454 1455static int iterate_postfix_command(int iter_cmd, int argc, char **argv, 1456 INST_SELECTION *selection) 1457{ 1458 int exit_status; 1459 char *cmd; 1460 ARGV *my_argv; 1461 int iter_flags; 1462 1463 /* 1464 * Override the iterator controls. 1465 */ 1466 if (word_in_list(var_multi_start_cmds, argv[0])) { 1467 iter_flags = ITER_FLAG_CHECK_DISABLED; 1468 } else if (word_in_list(var_multi_stop_cmds, argv[0])) { 1469 iter_flags = ITER_FLAG_SKIP_DISABLED | ITER_FLAG_REVERSE; 1470 } else if (word_in_list(var_multi_cntrl_cmds, argv[0])) { 1471 iter_flags = ITER_FLAG_SKIP_DISABLED; 1472 } else { 1473 iter_flags = 0; 1474 } 1475 1476 /* 1477 * Override the command line in a straightforward manner: prepend 1478 * "postfix --" to the command arguments. Other overrides (environment, 1479 * start -> check) are implemented below the iterator. 1480 */ 1481#define POSTFIX_CMD "postfix" 1482 1483 my_argv = argv_alloc(argc + 2); 1484 cmd = concatenate(var_command_dir, "/" POSTFIX_CMD, (char *) 0); 1485 argv_add(my_argv, cmd, "--", (char *) 0); 1486 myfree(cmd); 1487 while (*argv) 1488 argv_add(my_argv, *argv++, (char *) 0); 1489 1490 /* 1491 * Execute the command for all applicable Postfix instances. 1492 */ 1493 exit_status = 1494 iterate_command(iter_cmd, iter_flags, my_argv->argv, selection); 1495 1496 argv_free(my_argv); 1497 return (exit_status); 1498} 1499 1500/* list_instances - list all selected instances */ 1501 1502static void list_instances(int iter_flags, INST_SELECTION *selection) 1503{ 1504 RING *entry; 1505 INSTANCE *ip; 1506 1507 /* 1508 * Iterate over the selected instances. 1509 */ 1510 FOREACH_ITERATOR_INSTANCE(iter_flags, entry) { 1511 ip = RING_TO_INSTANCE(entry); 1512 if (match_instance_selection(ip, selection)) 1513 vstream_printf("%-15s %-15s %-9s %s\n", 1514 ip->name ? ip->name : "-", 1515 ip->gname ? ip->gname : "-", 1516 ip->enabled ? "y" : "n", 1517 ip->config_dir); 1518 } 1519 if (vstream_fflush(VSTREAM_OUT)) 1520 msg_fatal("error writing output: %m"); 1521} 1522 1523/* iterate_command - execute command for selected instances */ 1524 1525static int iterate_command(int iter_cmd, int iter_flags, char **argv, 1526 INST_SELECTION *selection) 1527{ 1528 int exit_status = 0; 1529 int matched = 0; 1530 RING *entry; 1531 INSTANCE *ip; 1532 1533 /* 1534 * Iterate over the selected instances. 1535 */ 1536 FOREACH_ITERATOR_INSTANCE(iter_flags, entry) { 1537 ip = RING_TO_INSTANCE(entry); 1538 if ((iter_flags & ITER_FLAG_SKIP_DISABLED) && !ip->enabled) 1539 continue; 1540 if (!match_instance_selection(ip, selection)) 1541 continue; 1542 matched = 1; 1543 1544 /* Run the requested command */ 1545 if (run_user_command(ip, iter_cmd, iter_flags, argv) != 0) 1546 exit_status = 1; 1547 } 1548 if (matched == 0) 1549 msg_fatal("No matching instances"); 1550 1551 return (exit_status); 1552} 1553 1554/* iterate - Iterate over all or selected instances */ 1555 1556static NORETURN iterate(int iter_cmd, int iter_flags, int argc, char **argv, 1557 INST_SELECTION *selection) 1558{ 1559 int exit_status; 1560 1561 /* 1562 * In iterator mode, no selection means wild-card selection. 1563 */ 1564 if (selection->type == INST_SEL_NONE) 1565 selection->type = INST_SEL_ALL; 1566 1567 /* 1568 * Load the in-memory instance table from main.cf files. 1569 */ 1570 load_all_instances(); 1571 1572 /* 1573 * Iterate over the selected instances. 1574 */ 1575 switch (iter_cmd) { 1576 case ITER_CMD_POSTFIX: 1577 exit_status = iterate_postfix_command(iter_cmd, argc, argv, selection); 1578 break; 1579 case ITER_CMD_LIST: 1580 list_instances(iter_flags, selection); 1581 exit_status = 0; 1582 break; 1583 case ITER_CMD_GENERIC: 1584 exit_status = iterate_command(iter_cmd, iter_flags, argv, selection); 1585 break; 1586 default: 1587 msg_panic("iterate: unknown mode: %d", iter_cmd); 1588 } 1589 exit(exit_status); 1590} 1591 1592static NORETURN usage(const char *progname) 1593{ 1594 msg_fatal("Usage:" 1595 "%s -l [-v] [-a] [-g group] [-i instance] | " 1596 "%s -p [-v] [-a] [-g group] [-i instance] command... | " 1597 "%s -x [-v] [-a] [-i name] [-g group] command... | " 1598 "%s -e action [-v] [-a] [-i name] [-g group] [-I name] " 1599 "[-G group] [param=value ...]", 1600 progname, progname, progname, progname); 1601} 1602 1603MAIL_VERSION_STAMP_DECLARE; 1604 1605/* main - iterate commands over multiple instance or manage instances */ 1606 1607int main(int argc, char **argv) 1608{ 1609 int fd; 1610 struct stat st; 1611 char *slash; 1612 char *config_dir; 1613 int ch; 1614 static const CONFIG_STR_TABLE str_table[] = { 1615 VAR_MULTI_START_CMDS, DEF_MULTI_START_CMDS, &var_multi_start_cmds, 0, 0, 1616 VAR_MULTI_STOP_CMDS, DEF_MULTI_STOP_CMDS, &var_multi_stop_cmds, 0, 0, 1617 VAR_MULTI_CNTRL_CMDS, DEF_MULTI_CNTRL_CMDS, &var_multi_cntrl_cmds, 0, 0, 1618 0, 1619 }; 1620 int instance_select_count = 0; 1621 int command_mode_count = 0; 1622 INST_SELECTION selection; 1623 NAME_ASSIGNMENT assignment; 1624 int iter_flags = ITER_FLAG_DEFAULT; 1625 int cmd_mode = 0; 1626 int code; 1627 1628 selection.type = INST_SEL_NONE; 1629 assignment.name = assignment.gname = 0; 1630 1631 /* 1632 * Fingerprint executables and core dumps. 1633 */ 1634 MAIL_VERSION_STAMP_ALLOCATE; 1635 1636 /* 1637 * Be consistent with file permissions. 1638 */ 1639 umask(022); 1640 1641 /* 1642 * To minimize confusion, make sure that the standard file descriptors 1643 * are open before opening anything else. XXX Work around for 44BSD where 1644 * fstat can return EBADF on an open file descriptor. 1645 */ 1646 for (fd = 0; fd < 3; fd++) 1647 if (fstat(fd, &st) == -1 1648 && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) 1649 msg_fatal("open /dev/null: %m"); 1650 1651 /* 1652 * Set up diagnostics. XXX What if stdin is the system console during 1653 * boot time? It seems a bad idea to log startup errors to the console. 1654 * This is UNIX, a system that can run without hand holding. 1655 */ 1656 if ((slash = strrchr(argv[0], '/')) != 0 && slash[1]) 1657 argv[0] = slash + 1; 1658 if (isatty(STDERR_FILENO)) 1659 msg_vstream_init(argv[0], VSTREAM_ERR); 1660 msg_syslog_init(argv[0], LOG_PID, LOG_FACILITY); 1661 1662 /* 1663 * Check the Postfix library version as soon as we enable logging. 1664 */ 1665 MAIL_VERSION_CHECK; 1666 1667 if ((config_dir = getenv(CONF_ENV_PATH)) != 0 1668 && strcmp(config_dir, DEF_CONFIG_DIR) != 0) 1669 msg_fatal("Non-default configuration directory: %s=%s", 1670 CONF_ENV_PATH, config_dir); 1671 1672 /* 1673 * Parse switches. 1674 */ 1675 while ((ch = GETOPT(argc, argv, "ae:g:i:G:I:lpRvx")) > 0) { 1676 switch (ch) { 1677 default: 1678 usage(argv[0]); 1679 /* NOTREACHED */ 1680 case 'a': 1681 if (selection.type != INST_SEL_ALL) 1682 instance_select_count++; 1683 selection.type = INST_SEL_ALL; 1684 break; 1685 case 'e': 1686 if ((code = EDIT_CMD_CODE(optarg)) < 0) 1687 msg_fatal("Invalid '-e' edit action '%s'. Specify '%s', " 1688 "'%s', '%s', '%s', '%s', '%s', '%s', '%s' or '%s'", 1689 optarg, 1690 EDIT_CMD_STR(EDIT_CMD_CREATE), 1691 EDIT_CMD_STR(EDIT_CMD_DESTROY), 1692 EDIT_CMD_STR(EDIT_CMD_IMPORT), 1693 EDIT_CMD_STR(EDIT_CMD_DEPORT), 1694 EDIT_CMD_STR(EDIT_CMD_ENABLE), 1695 EDIT_CMD_STR(EDIT_CMD_DISABLE), 1696 EDIT_CMD_STR(EDIT_CMD_ASSIGN), 1697 EDIT_CMD_STR(EDIT_CMD_INIT), 1698 optarg); 1699 if (cmd_mode != code) 1700 command_mode_count++; 1701 cmd_mode = code; 1702 break; 1703 case 'g': 1704 instance_select_count++; 1705 selection.type = INST_SEL_GROUP; 1706 selection.name = optarg; 1707 break; 1708 case 'i': 1709 instance_select_count++; 1710 selection.type = INST_SEL_NAME; 1711 selection.name = optarg; 1712 break; 1713 case 'G': 1714 if (assignment.gname != 0) 1715 msg_fatal("Specify at most one '-G' option"); 1716 assignment.gname = strcmp(optarg, "-") == 0 ? "" : optarg; 1717 break; 1718 case 'I': 1719 if (assignment.name != 0) 1720 msg_fatal("Specify at most one '-I' option"); 1721 assignment.name = strcmp(optarg, "-") == 0 ? "" : optarg; 1722 break; 1723 case 'l': 1724 if (cmd_mode != ITER_CMD_LIST) 1725 command_mode_count++; 1726 cmd_mode = ITER_CMD_LIST; 1727 break; 1728 case 'p': 1729 if (cmd_mode != ITER_CMD_POSTFIX) 1730 command_mode_count++; 1731 cmd_mode = ITER_CMD_POSTFIX; 1732 break; 1733 case 'R': 1734 iter_flags ^= ITER_FLAG_REVERSE; 1735 break; 1736 case 'v': 1737 msg_verbose++; 1738 check_setenv(CONF_ENV_VERB, ""); 1739 break; 1740 case 'x': 1741 if (cmd_mode != ITER_CMD_GENERIC) 1742 command_mode_count++; 1743 cmd_mode = ITER_CMD_GENERIC; 1744 break; 1745 } 1746 } 1747 1748 /* 1749 * Report missing arguments, or wrong arguments in the wrong context. 1750 */ 1751 if (instance_select_count > 1) 1752 msg_fatal("Specity no more than one of '-a', '-g', '-i'"); 1753 1754 if (command_mode_count != 1) 1755 msg_fatal("Specify exactly one of '-e', '-l', '-p', '-x'"); 1756 1757 if (cmd_mode == ITER_CMD_LIST && argc > optind) 1758 msg_fatal("Command not allowed with '-l'"); 1759 1760 if (cmd_mode == ITER_CMD_POSTFIX || cmd_mode == ITER_CMD_GENERIC) 1761 if (argc == optind) 1762 msg_fatal("Command required with '-p' or '-x' option"); 1763 1764 if (cmd_mode == ITER_CMD_POSTFIX || (cmd_mode & EDIT_CMD_MASK_ALL)) 1765 if (iter_flags != ITER_FLAG_DEFAULT) 1766 msg_fatal("The '-p' and '-e' options preclude the use of '-R'"); 1767 1768 if ((cmd_mode & EDIT_CMD_MASK_ASSIGN) == 0 1769 && (assignment.name || assignment.gname)) { 1770 if ((cmd_mode & EDIT_CMD_MASK_ALL) == 0) 1771 msg_fatal("Cannot assign instance name or group without '-e %s'", 1772 EDIT_CMD_STR(EDIT_CMD_ASSIGN)); 1773 else 1774 msg_fatal("Cannot assign instance name or group with '-e %s'", 1775 EDIT_CMD_STR(cmd_mode)); 1776 } 1777 if (cmd_mode & EDIT_CMD_MASK_ALL) { 1778 if (cmd_mode == EDIT_CMD_ASSIGN 1779 && (assignment.name == 0 && assignment.gname == 0)) 1780 msg_fatal("Specify new instance name or group with '-e %s'", 1781 EDIT_CMD_STR(cmd_mode)); 1782 1783 if ((cmd_mode & ~EDIT_CMD_MASK_ADD) != 0 && argc > optind) 1784 msg_fatal("Parameter overrides not valid with '-e %s'", 1785 EDIT_CMD_STR(cmd_mode)); 1786 } 1787 1788 /* 1789 * Proces main.cf parameters. 1790 */ 1791 mail_conf_read(); 1792 get_mail_conf_str_table(str_table); 1793 1794 /* 1795 * Sanity checks. 1796 */ 1797 check_shared_dir_status(); 1798 1799 /* 1800 * Iterate over selected instances, or manipulate one instance. 1801 */ 1802 if (cmd_mode & ITER_CMD_MASK_ALL) 1803 iterate(cmd_mode, iter_flags, argc - optind, argv + optind, &selection); 1804 else 1805 manage(cmd_mode, argc - optind, argv + optind, &selection, &assignment); 1806} 1807