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