407 #include <sys/stat.h>
408 #include <sys/wait.h>
474 #define ITER_CMD_POSTFIX (1<<0)
475 #define ITER_CMD_LIST (1<<1)
476 #define ITER_CMD_GENERIC (1<<2)
478 #define ITER_CMD_MASK_ALL \
479 (ITER_CMD_POSTFIX | ITER_CMD_LIST | ITER_CMD_GENERIC)
481 #define EDIT_CMD_CREATE (1<<4)
482 #define EDIT_CMD_IMPORT (1<<5)
483 #define EDIT_CMD_DESTROY (1<<6)
484 #define EDIT_CMD_DEPORT (1<<7)
485 #define EDIT_CMD_ENABLE (1<<8)
486 #define EDIT_CMD_DISABLE (1<<9)
487 #define EDIT_CMD_ASSIGN (1<<10)
488 #define EDIT_CMD_INIT (1<<11)
490 #define EDIT_CMD_MASK_ADD (EDIT_CMD_CREATE | EDIT_CMD_IMPORT)
491 #define EDIT_CMD_MASK_DEL (EDIT_CMD_DESTROY | EDIT_CMD_DEPORT)
492 #define EDIT_CMD_MASK_ASSIGN (EDIT_CMD_MASK_ADD | EDIT_CMD_ASSIGN)
493 #define EDIT_CMD_MASK_ENB (EDIT_CMD_ENABLE | EDIT_CMD_DISABLE)
494 #define EDIT_CMD_MASK_ALL \
495 (EDIT_CMD_MASK_ASSIGN | EDIT_CMD_MASK_DEL | EDIT_CMD_MASK_ENB | \
501 static NAME_CODE edit_command_table[] = {
513 #define EDIT_CMD_CODE(str) \
514 name_code(edit_command_table, NAME_CODE_FLAG_STRICT_CASE, (str))
515 #define EDIT_CMD_STR(code) str_name_code(edit_command_table, (code))
521 #define NAME_PREFIX "postfix-"
523 #define HAS_NAME_PREFIX(name) \
524 (strncmp((name), NAME_PREFIX, sizeof(NAME_PREFIX)-1) == 0)
525 #define NEED_NAME_PREFIX(name) \
526 ((name) != 0 && strcmp((name), "-") != 0 && !HAS_NAME_PREFIX(name))
527 #define NAME_SUFFIX(name) ((name) + sizeof(NAME_PREFIX) - 1)
546 static RING instance_hd[1];
548 #define RING_TO_INSTANCE(ring_ptr) RING_TO_APPL(ring_ptr, INSTANCE, ring)
549 #define RING_PTR_OF(x) (&((x)->ring))
551 #define FOREACH_INSTANCE(entry) \
552 for ((entry) = instance_hd; \
553 ((entry) = ring_succ(entry)) != instance_hd;)
555 #define FOREACH_SECONDARY_INSTANCE(entry) \
556 for ((entry) = ring_succ(instance_hd); \
557 ((entry) = ring_succ(entry)) != instance_hd;)
559 #define NEXT_ITERATOR_INSTANCE(flags, entry) \
560 (((flags) & ITER_FLAG_REVERSE) ? ring_pred(entry) : ring_succ(entry))
562 #define FOREACH_ITERATOR_INSTANCE(flags, entry) \
563 for ((entry) = instance_hd; \
564 ((entry) = NEXT_ITERATOR_INSTANCE(flags, (entry))) != instance_hd;)
575 #define INST_SEL_NONE 0
576 #define INST_SEL_ALL 1
577 #define INST_SEL_NAME 2
578 #define INST_SEL_GROUP 3
596 #define ITER_FLAG_DEFAULT 0
597 #define ITER_FLAG_REVERSE (1<<0)
598 #define ITER_FLAG_CHECK_DISABLED (1<<1)
599 #define ITER_FLAG_SKIP_DISABLED (1<<2)
605 #define EXP_FLAG_MULTI_DIRS (1<<0)
606 #define EXP_FLAG_MULTI_NAME (1<<1)
607 #define EXP_FLAG_MULTI_GROUP (1<<2)
621 static HTABLE *claim_table;
623 #define IS_CLAIMED_BY(name) \
624 (claim_table ? htable_find(claim_table, (name)) : 0)
635 #define INSTANCE_NAME(i) ((i)->name ? (i)->name : (i)->config_dir)
636 #define STR(buf) vstring_str(buf)
640 static void register_claim(
const char *instance_path,
const char *param_name,
641 const char *param_value)
643 const char *myname =
"register_claim";
650 if (instance_path == 0 || *instance_path == 0)
651 msg_panic(
"%s: no or empty instance pathname", myname);
652 if (param_name == 0 || *param_name == 0)
653 msg_panic(
"%s: no or empty parameter name", myname);
654 if (param_value == 0)
655 msg_panic(
"%s: no parameter value", myname);
660 if (claim_table == 0)
662 requestor =
concatenate(instance_path,
", ", param_name, (
char *) 0);
663 if ((owner =
htable_find(claim_table, param_value)) == 0) {
664 (void)
htable_enter(claim_table, param_value, requestor);
665 }
else if (strcmp(owner, requestor) == 0) {
668 msg_fatal(
"instance %s, %s=%s conflicts with instance %s=%s",
669 instance_path, param_name, param_value, owner, param_value);
675 static void claim_instance_attributes(
INSTANCE *ip)
691 static INSTANCE *alloc_instance(
const char *config_dir)
711 static void free_instance(
INSTANCE *ip)
739 #define append_instance(ip) insert_instance((ip), (INST_SELECTION *) 0)
744 claim_instance_attributes(ip);
755 msg_fatal(
"No matching secondary instances");
762 static INSTANCE *create_primary_instance(
void)
770 #define SAVE_INSTANCE_NAME(val) (*(val) ? mystrdup(val) : 0)
800 #define REQUEST_PARAM_COUNT 5
823 msg_fatal(
"Invalid %s/main.cf parameter: %s",
837 msg_fatal(
"Unexpected %s/main.cf entry: %s = %s",
850 msg_fatal(
"Failed to obtain all required %s/main.cf parameters",
860 static void load_all_instances(
void)
864 ARGV *secondary_names;
876 primary_instance = create_primary_instance();
877 if (secondary_names->
argc == 0)
887 for (cpp = secondary_names->
argv; *cpp != 0; cpp++)
906 switch (selection->
type) {
912 return (ip->
gname != 0 && strcmp(selection->
name, ip->
gname) == 0);
914 name = selection->
name;
915 if (*name ==
'/' || ip->
name == 0)
921 return (strcmp(name, iname) == 0
922 || (ip->
primary && strcmp(name,
"-") == 0));
924 msg_panic(
"match_instance_selection: unknown selection type: %d",
931 static void check_setenv(
const char *name,
const char *value)
934 if (setenv(name, value,
CLOBBER) < 0)
940 static void prepend_command_path(
void)
950 cmd_path : ROOT_PATH, (
char *) 0);
951 check_setenv(
"PATH", cmd_path);
957 static void check_shared_dir_status(
void)
967 for (sp = shared_dir_table; sp->
param_name; ++sp) {
968 if (sp->param_value[0][0] !=
'/')
970 if (
stat(sp->param_value[0], &st) < 0)
971 msg_fatal(
"%s = '%s': directory not found: %m",
972 sp->param_name, sp->param_value[0]);
973 if (!S_ISDIR(st.st_mode))
974 msg_fatal(
"%s = '%s' is not a directory",
975 sp->param_name, sp->param_value[0]);
978 register_claim(
var_config_dir, sp->param_name, sp->param_value[0]);
984 static int check_safe_name(
const char *s)
986 #define SAFE_PUNCT "!@%-_=+:./"
1008 if (assignment->
name && *assignment->
name) {
1009 if (!check_safe_name(assignment->
name))
1010 msg_fatal(
"Unsafe characters in new instance name: '%s'",
1012 if (strchr(assignment->
name,
'/'))
1013 msg_fatal(
"Illegal '/' character in new instance name: '%s'",
1016 msg_fatal(
"New instance name must start with '%s'",
1023 if (assignment->
gname && *assignment->
gname) {
1024 if (!check_safe_name(assignment->
gname))
1033 int export_flags = 0;
1039 if (assignment->
name
1040 && strcmp(assignment->
name, target->
name ? target->
name :
"")) {
1047 if (assignment->
gname
1054 return (export_flags);
1059 static char *make_private_path(
const char *param_name,
1060 const char *primary_value,
1071 if (assignment->
name == 0 || *assignment->
name == 0)
1072 msg_fatal(
"Missing %s parameter value", param_name);
1074 if (*primary_value !=
'/')
1075 msg_fatal(
"Invalid default %s parameter value: '%s': "
1076 "specify an absolute pathname",
1077 param_name, primary_value);
1080 if ((end = strrchr(base,
'/')) != 0) {
1082 if (end[1] ==
'\0') {
1083 while (--end > base && *end ==
'/')
1085 end = strrchr(base,
'/');
1088 while (end > base && *end ==
'/')
1092 assignment->
name, (
char *) 0);
1099 static void assign_new_parameter(
INSTANCE *
new,
int edit_cmd,
1119 msg_fatal(
"Malformed parameter setting '%s'", arg);
1122 target = &
new->config_dir;
1125 target = &
new->queue_dir;
1127 target = &
new->data_dir;
1131 msg_fatal(
"Parameter '%s' not valid with action %s",
1140 msg_fatal(
"Parameter setting '%s' is not an absolute path", name);
1143 for (end = value + strlen(value) - 1; end > value && *end ==
'/'; --end)
1148 msg_fatal(
"Parameter setting '%s' is the root directory", name);
1162 static void assign_new_parameters(
INSTANCE *
new,
int edit_cmd,
1172 assign_new_parameter(
new, edit_cmd, *argv++);
1182 if (new->config_dir == 0)
1187 msg_fatal(
"new %s=%s is already in use by instance %s=%s",
1190 if (new->queue_dir == 0)
1193 if (new->data_dir == 0)
1203 static void export_helper_environment(
INSTANCE *target,
int export_flags)
1224 prepend_command_path();
1235 for (sp = shared_dir_table; sp->
param_name; ++sp)
1267 target->
name :
"-");
1271 target->
gname :
"-");
1282 static INSTANCE *install_new_instance(
int edit_cmd,
char **argv,
1289 new = alloc_instance((
char *) 0);
1290 check_name_assignments(assignment);
1291 assign_new_parameters(
new, edit_cmd, argv, assignment);
1294 insert_instance(
new, selection);
1304 check_name_assignments(assignment);
1305 export_flags = do_name_assignments(target, assignment);
1306 return (export_flags);
1319 #define DONT_UNLINK 0
1323 msg_fatal(
"Select an instance via '-i name'");
1339 msg_fatal(
"Cannot remove the primary instance");
1341 msg_fatal(
"Cannot remove enabled instances");
1343 if (export_flags == 0)
1344 msg_panic(
"select_existing_instance: no export flags");
1352 static NORETURN manage(
int edit_cmd,
int argc,
char **argv,
1363 #define NO_EXPORT_FLAGS ((int *) 0)
1368 target = create_primary_instance();
1373 load_all_instances();
1374 target = install_new_instance(edit_cmd, argv, selection,
1375 assignment, &export_flags);
1379 load_all_instances();
1382 export_flags |= update_instance(target, assignment);
1383 if (export_flags == 0)
1389 load_all_instances();
1390 target = select_existing_instance(selection,
DO_UNLINK, &export_flags);
1394 load_all_instances();
1404 #define HELPER "postmulti-script"
1406 export_helper_environment(target, export_flags);
1408 execl(cmd, cmd,
"-e",
EDIT_CMD_STR(edit_cmd), (
char *) 0);
1414 static int run_user_command(
INSTANCE *ip,
int iter_cmd,
int iter_flags,
1431 switch (pid = fork()) {
1447 prepend_command_path();
1457 execvp(argv[0], argv);
1461 wpid = waitpid(pid, &status, 0);
1462 }
while (wpid == -1 && errno == EINTR);
1463 return (wpid == -1 ? -1 :
1464 WIFEXITED(status) ? WEXITSTATUS(status) : 1);
1470 static int word_in_list(
char *cmdlist,
const char *cmd)
1485 static int iterate_postfix_command(
int iter_cmd,
int argc,
char **argv,
1511 #define POSTFIX_CMD "postfix"
1515 argv_add(my_argv, cmd,
"--", (
char *) 0);
1518 argv_add(my_argv, *argv++, (
char *) 0);
1524 iterate_command(iter_cmd, iter_flags, my_argv->
argv, selection);
1527 return (exit_status);
1532 static void list_instances(
int iter_flags,
INST_SELECTION *selection)
1542 if (match_instance_selection(ip, selection))
1555 static int iterate_command(
int iter_cmd,
int iter_flags,
char **argv,
1558 int exit_status = 0;
1570 if (!match_instance_selection(ip, selection))
1575 if (run_user_command(ip, iter_cmd, iter_flags, argv) != 0)
1581 return (exit_status);
1586 static NORETURN iterate(
int iter_cmd,
int iter_flags,
int argc,
char **argv,
1600 load_all_instances();
1607 exit_status = iterate_postfix_command(iter_cmd, argc, argv, selection);
1610 list_instances(iter_flags, selection);
1614 exit_status = iterate_command(iter_cmd, iter_flags, argv, selection);
1617 msg_panic(
"iterate: unknown mode: %d", iter_cmd);
1622 static NORETURN usage(
const char *progname)
1625 "%s -l [-v] [-a] [-g group] [-i instance] | "
1626 "%s -p [-v] [-a] [-g group] [-i instance] command... | "
1627 "%s -x [-v] [-a] [-i name] [-g group] command... | "
1628 "%s -e action [-v] [-a] [-i name] [-g group] [-I name] "
1629 "[-G group] [param=value ...]",
1630 progname, progname, progname, progname);
1650 int instance_select_count = 0;
1651 int command_mode_count = 0;
1676 for (fd = 0; fd < 3; fd++)
1677 if (
fstat(fd, &st) == -1
1678 && (close(fd), open(
"/dev/null", O_RDWR, 0)) != fd)
1686 if ((slash = strrchr(argv[0],
'/')) != 0 && slash[1])
1687 argv[0] = slash + 1;
1688 if (isatty(STDERR_FILENO))
1699 msg_fatal(
"Non-default configuration directory: %s=%s",
1705 while ((ch =
GETOPT(argc, argv,
"ae:g:i:G:I:lpRvx")) > 0) {
1712 instance_select_count++;
1717 msg_fatal(
"Invalid '-e' edit action '%s'. Specify '%s', "
1718 "'%s', '%s', '%s', '%s', '%s', '%s' or '%s'",
1728 if (cmd_mode != code)
1729 command_mode_count++;
1733 instance_select_count++;
1735 selection.
name = optarg;
1738 instance_select_count++;
1740 selection.
name = optarg;
1743 if (assignment.
gname != 0)
1744 msg_fatal(
"Specify at most one '-G' option");
1745 assignment.
gname = strcmp(optarg,
"-") == 0 ?
"" : optarg;
1748 if (assignment.
name != 0)
1749 msg_fatal(
"Specify at most one '-I' option");
1750 assignment.
name = strcmp(optarg,
"-") == 0 ?
"" : optarg;
1754 command_mode_count++;
1759 command_mode_count++;
1771 command_mode_count++;
1780 if (instance_select_count > 1)
1781 msg_fatal(
"Specity no more than one of '-a', '-g', '-i'");
1783 if (command_mode_count != 1)
1784 msg_fatal(
"Specify exactly one of '-e', '-l', '-p', '-x'");
1787 msg_fatal(
"Command not allowed with '-l'");
1791 msg_fatal(
"Command required with '-p' or '-x' option");
1795 msg_fatal(
"The '-p' and '-e' options preclude the use of '-R'");
1798 && (assignment.
name || assignment.
gname)) {
1799 if ((cmd_mode & EDIT_CMD_MASK_ALL) == 0)
1800 msg_fatal(
"Cannot assign instance name or group without '-e %s'",
1803 msg_fatal(
"Cannot assign instance name or group with '-e %s'",
1806 if (cmd_mode & EDIT_CMD_MASK_ALL) {
1808 && (assignment.
name == 0 && assignment.
gname == 0))
1809 msg_fatal(
"Specify new instance name or group with '-e %s'",
1813 msg_fatal(
"Parameter overrides not valid with '-e %s'",
1826 check_shared_dir_status();
1832 iterate(cmd_mode, iter_flags, argc - optind, argv + optind, &selection);
1834 manage(cmd_mode, argc - optind, argv + optind, &selection, &assignment);
#define EXP_FLAG_MULTI_GROUP
#define DEF_MULTI_START_CMDS
void ring_detach(RING *entry)
#define NAME_SUFFIX(name)
#define ITER_FLAG_CHECK_DISABLED
char * mystrdup(const char *str)
char * var_import_environ
int vstring_get_nonl(VSTRING *vp, VSTREAM *fp)
ARGV * argv_free(ARGV *argvp)
#define VAR_IMPORT_ENVIRON
NORETURN msg_panic(const char *fmt,...)
#define append_instance(ip)
void ring_init(RING *ring)
#define IS_CLAIMED_BY(name)
#define CA_VSTREAM_POPEN_ARGV(v)
char * var_multi_conf_dirs
void argv_add(ARGV *argvp,...)
#define FOREACH_SECONDARY_INSTANCE(entry)
#define EDIT_CMD_CODE(str)
#define EXP_FLAG_MULTI_DIRS
char * mystrtok(char **src, const char *sep)
ARGV * argv_alloc(ssize_t len)
const char * split_nameval(char *buf, char **name, char **value)
#define ITER_FLAG_DEFAULT
void mail_conf_read(void)
int vstream_pclose(VSTREAM *)
void clean_env(char **preserve_list)
HTABLE * htable_create(ssize_t size)
#define VSTRING_ADDCH(vp, ch)
int main(int argc, char **argv)
#define DEF_MULTI_CNTRL_CMDS
ARGV * mail_parm_split(const char *name, const char *value)
#define VAR_MULTI_CONF_DIRS
#define VAR_MULTI_START_CMDS
#define VAR_MULTI_STOP_CMDS
VSTREAM * vstream_printf(const char *fmt,...)
#define EDIT_CMD_MASK_ALL
char * safe_getenv(const char *)
#define EDIT_CMD_STR(code)
#define DEF_MULTI_STOP_CMDS
void msg_warn(const char *fmt,...)
VSTRING * vstring_alloc(ssize_t len)
#define CA_VSTREAM_POPEN_END
#define NAME_CODE_FLAG_NONE
#define MAIL_VERSION_STAMP_ALLOCATE
#define HAS_NAME_PREFIX(name)
#define NEED_NAME_PREFIX(name)
void * htable_find(HTABLE *table, const char *key)
char * var_multi_stop_cmds
#define RING_TO_INSTANCE(ring_ptr)
MAIL_VERSION_STAMP_DECLARE
#define VAR_MULTI_CNTRL_CMDS
#define FOREACH_ITERATOR_INSTANCE(flags, entry)
void get_mail_conf_str_table(const CONFIG_STR_TABLE *)
int name_code(const NAME_CODE *table, int flags, const char *name)
NORETURN msg_fatal(const char *fmt,...)
#define ITER_CMD_MASK_ALL
#define MAIL_VERSION_CHECK
#define EDIT_CMD_MASK_ASSIGN
int vstream_fflush(VSTREAM *stream)
char * concatenate(const char *arg0,...)
ARGV * argv_split(const char *, const char *)
#define GETOPT(argc, argv, str)
void msg_syslog_init(const char *name, int logopt, int facility)
#define EDIT_CMD_MASK_ADD
#define ITER_FLAG_SKIP_DISABLED
VSTRING * vstring_free(VSTRING *vp)
void msg_vstream_init(const char *name, VSTREAM *vp)
#define SAVE_INSTANCE_NAME(val)
VSTREAM VSTREAM const char VSTREAM * vstream_popen(int,...)
#define REQUEST_PARAM_COUNT
char * var_multi_start_cmds
#define FOREACH_INSTANCE(entry)
VSTRING * vstring_strcat(VSTRING *vp, const char *src)
char * var_multi_cntrl_cmds
#define ITER_FLAG_REVERSE
void * mymalloc(ssize_t len)
#define EXP_FLAG_MULTI_NAME
HTABLE_INFO * htable_enter(HTABLE *table, const char *key, void *value)
void ring_prepend(RING *ring, RING *entry)