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