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