aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2009-11-21 01:46:47 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2009-11-21 01:46:47 +0200
commit118f9d898f8e0208aa01275cf37752b888c39d20 (patch)
tree93c29187b3a8db6e71b12da2f45e96c38cde2baa
parentc9f9835a1820fa85ae49f2f511ce793255618bcb (diff)
downloadmailfromd-118f9d898f8e0208aa01275cf37752b888c39d20.tar.gz
mailfromd-118f9d898f8e0208aa01275cf37752b888c39d20.tar.bz2
Implement multiple servers support.
* mfd/srvman.c: New file. * mfd/srvman.h: New file. * gacopyz/gacopyz.h (gacopyz_context_loop): New proto. * gacopyz/gacopyz.c (gacopyz_context_loop): Remove static qualifier. Take additional parameter (closure). All callers updated. (gacopyz_getclosure): New proto. * gacopyz/gacopyz_priv.h (struct smfi_str): desc is const *. (closure): New member. * mfd/Makefile.am (mailfromd_SOURCES): Add srvman.c and srvman.h. * mfd/bi_vars.m4: New MFL variable milter_server_id. (set_milter_server_id): New proto. * mfd/engine.c (priv_get): Set milter_server_id. (milter_conn_accept): Remove. (smfilter): Static. Init xxfi_accept to NULL. (milter_session_server, milter_setlogmask) (milter_settimeout): New functions. (check_local_portspec, check_portspec): Remove. (sig_stop, sig_restart): Call mfd_srvman_stop instead of smfi_stop. (mailfromd_daemon): Rewrite via srvman functions. * mfd/mailfromd.h (portspec, mailfromd_acl): Remove. (add_legacy_milter_port, free_parser_data) (set_milter_server_id) (milter_session_server, milter_setlogmask) (milter_settimeout): New prototypes. * mfd/main.c (portspec): Remove (force_remove): Initialize to 1. (mailfromd_acl): Remove. (parse_milter_url, next_server_id) (add_legacy_milter_port): New functions. (set_port): Call add_legacy_milter_port. (parse_opt): Call milter_set.* functions, instead of smfi_set.* (portspec_fixup): Remove. (main): Call milter_cfg_init. (mf_cfg_param): New block statement 'milter'. * mflib/Makefile.am (check-am): Ignore configuration files.
-rw-r--r--gacopyz/context.c8
-rw-r--r--gacopyz/gacopyz.c11
-rw-r--r--gacopyz/gacopyz.h7
-rw-r--r--gacopyz/gacopyz_priv.h3
-rw-r--r--mfd/Makefile.am2
-rw-r--r--mfd/bi_vars.m49
-rw-r--r--mfd/engine.c161
-rw-r--r--mfd/mailfromd.h13
-rw-r--r--mfd/main.c280
-rw-r--r--mfd/srvman.c810
-rw-r--r--mfd/srvman.h68
-rw-r--r--mflib/Makefile.am7
12 files changed, 1194 insertions, 185 deletions
diff --git a/gacopyz/context.c b/gacopyz/context.c
index 369f15cb..a1c13efc 100644
--- a/gacopyz/context.c
+++ b/gacopyz/context.c
@@ -33,6 +33,14 @@ gacopyz_getpriv (SMFICTX *ctx)
return ctx->privdata;
}
+void *
+gacopyz_getclosure(SMFICTX *ctx)
+{
+ if (!ctx)
+ return NULL;
+ return ctx->closure;
+}
+
int
gacopyz_server_sockname(SMFICTX *ctx,
milter_sockaddr_t *name, socklen_t *namelen)
diff --git a/gacopyz/gacopyz.c b/gacopyz/gacopyz.c
index 0a1979f0..4b363171 100644
--- a/gacopyz/gacopyz.c
+++ b/gacopyz/gacopyz.c
@@ -165,7 +165,6 @@ parse_connection(gacopyz_conn_t conn,
gacopyz_log(SMI_LOG_ERR,
"parse_connection: %s", strerror(ENOMEM));
return rc;
-
}
static void
@@ -1500,9 +1499,10 @@ ctx_free(SMFICTX *ctx)
free(ctx->req_macros[i]);
}
-static int
-gacopyz_context_loop(int fd, struct smfiDesc *desc,
- milter_sockaddr_t *addr, socklen_t addrlen)
+int
+gacopyz_context_loop(int fd, struct smfiDesc const *desc,
+ milter_sockaddr_t *addr, socklen_t addrlen,
+ void *closure)
{
int rc;
char *buffer = NULL;
@@ -1522,6 +1522,7 @@ gacopyz_context_loop(int fd, struct smfiDesc *desc,
ctx.sd = fd;
ctx.addr = *addr;
ctx.addrlen = addrlen;
+ ctx.closure = closure;
if (!desc->xxfi_connect)
ctx.pflags |= SMFIP_NOCONNECT;
if (!desc->xxfi_helo)
@@ -1745,7 +1746,7 @@ gacopyz_handle_connection(gacopyz_conn_t conn)
addr.sa.sa_family);
}
- rc = gacopyz_context_loop(fd, &conn->desc, &addr, addrlen);
+ rc = gacopyz_context_loop(fd, &conn->desc, &addr, addrlen, NULL);
close(fd);
if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_INFO))
diff --git a/gacopyz/gacopyz.h b/gacopyz/gacopyz.h
index 232d1167..1f938788 100644
--- a/gacopyz/gacopyz.h
+++ b/gacopyz/gacopyz.h
@@ -342,6 +342,9 @@ int gacopyz_connect(gacopyz_conn_t *, struct smfiDesc *, const char *,
int gacopyz_get_fd(gacopyz_conn_t);
int gacopyz_run(gacopyz_conn_t);
+int gacopyz_context_loop(int fd, struct smfiDesc const *desc,
+ milter_sockaddr_t *addr, socklen_t addrlen,
+ void *calldata);
int gacopyz_register_child(gacopyz_conn_t conn, pid_t pid);
void gacopyz_cleanup_children(gacopyz_conn_t conn);
@@ -375,10 +378,12 @@ int gacopyz_quarantine(SMFICTX *ctx, const char *reason);
int gacopyz_add_rcpt_par(SMFICTX *ctx, const char *rcpt, const char *args);
int gacopyz_chgfrom(SMFICTX *ctx, const char *from, const char *args);
-int gacopyz_setsymlist(SMFICTX *ctx, enum gacopyz_stage ind, const char *macros);
+int gacopyz_setsymlist(SMFICTX *ctx, enum gacopyz_stage ind,
+ const char *macros);
int gacopyz_setpriv (SMFICTX *ctx, void *data);
void *gacopyz_getpriv (SMFICTX *ctx);
+void *gacopyz_getclosure(SMFICTX *ctx);
int gacopyz_client_sockname(SMFICTX *ctx,
milter_sockaddr_t *name, socklen_t *namelen);
diff --git a/gacopyz/gacopyz_priv.h b/gacopyz/gacopyz_priv.h
index 6f55e43f..c151082f 100644
--- a/gacopyz/gacopyz_priv.h
+++ b/gacopyz/gacopyz_priv.h
@@ -26,7 +26,7 @@ typedef struct macro_assoc {
} macro_assoc_t;
struct smfi_str {
- struct smfiDesc *desc; /* parent description */
+ struct smfiDesc const *desc; /* parent description */
int sd; /* socket descriptor */
milter_sockaddr_t addr;/* client address */
socklen_t addrlen; /* length of addr */
@@ -40,6 +40,7 @@ struct smfi_str {
char *req_macros[gacopyz_stage_max]; /* Required macros */
char *reply; /* reply code */
void *privdata; /* private data */
+ void *closure; /* call data */
};
struct gacopyz_conn {
diff --git a/mfd/Makefile.am b/mfd/Makefile.am
index 18e8bc8f..d1288a9a 100644
--- a/mfd/Makefile.am
+++ b/mfd/Makefile.am
@@ -72,6 +72,8 @@ mailfromd_SOURCES = \
pragma.c\
prog.c\
prog.h\
+ srvman.c\
+ srvman.h\
spf.c\
stack.c\
symbols.c\
diff --git a/mfd/bi_vars.m4 b/mfd/bi_vars.m4
index 9237f808..b88ce99b 100644
--- a/mfd/bi_vars.m4
+++ b/mfd/bi_vars.m4
@@ -21,7 +21,8 @@ MF_VAR(milter_client_family, NUMBER, SYM_PRECIOUS);
MF_VAR(milter_client_address, STRING, SYM_PRECIOUS);
MF_VAR(milter_server_family, NUMBER, SYM_PRECIOUS);
MF_VAR(milter_server_address, STRING, SYM_PRECIOUS);
-
+MF_VAR(milter_server_id, STRING, SYM_PRECIOUS);
+
/* Functions to access %rcpt_count */
unsigned long
get_rcpt_count(eval_environ_t env)
@@ -72,6 +73,12 @@ define_milter_address(server)
define_milter_address(client)
void
+set_milter_server_id(eval_environ_t env, const char *id)
+{
+ MF_VAR_SET_STRING(milter_server_id, id ? id : "");
+}
+
+void
builtin_post_setup()
{
struct value val;
diff --git a/mfd/engine.c b/mfd/engine.c
index d7962548..6c9b1cc6 100644
--- a/mfd/engine.c
+++ b/mfd/engine.c
@@ -38,6 +38,7 @@
#include <mailutils/daemon.h>
#include "mailfromd.h"
+#include "srvman.h"
static const char *ctx_getsym(void *data, const char *str);
@@ -95,6 +96,7 @@ priv_get(SMFICTX *ctx)
env_init(md->env);
if (gacopyz_server_sockname(ctx, &addr, &len) == 0)
set_milter_server_address(md->env, &addr, len);
+ set_milter_server_id(md->env, gacopyz_getclosure(ctx));
if (gacopyz_client_sockname(ctx, &addr, &len) == 0)
set_milter_client_address(md->env, &addr, len);
@@ -833,48 +835,6 @@ child_start()
return 0;
}
-static int
-milter_conn_accept (gacopyz_conn_t conn, int fd, const milter_sockaddr_t *msa,
- int len)
-{
- mu_acl_result_t res;
- int rc;
- char *p;
-
- if (!mailfromd_acl)
- return 0;
-
- rc = mu_acl_check_sockaddr (mailfromd_acl, &msa->sa, len, &res);
- if (rc) {
- p = mu_sockaddr_to_astr (&msa->sa, len);
- mu_error (_("access from %s blocked: cannot check ACLs: %s"),
- p, mu_strerror (rc));
- free (p);
- return 1;
- }
-
- switch (res) {
- case mu_acl_result_undefined:
- p = mu_sockaddr_to_astr (&msa->sa, len);
- mu_diag_output (MU_DIAG_INFO,
- _("%s: undefined ACL result; access allowed"),
- p);
- free (p);
- break;
-
- case mu_acl_result_accept:
- break;
-
- case mu_acl_result_deny:
- p = mu_sockaddr_to_astr (&msa->sa, len);
- mu_error (_("access from %s blocked."), p);
- free (p);
- return 1;
- }
-
- return 0;
-}
-
sfsistat
mlfi_negotiate(SMFICTX *ctx,
unsigned long mta_actions,
@@ -900,7 +860,7 @@ mlfi_negotiate(SMFICTX *ctx,
return SMFIS_CONTINUE;
}
-struct smfiDesc smfilter =
+static struct smfiDesc smfilter =
{
"MailfromFilter",
SMFI_VERSION,
@@ -923,10 +883,37 @@ struct smfiDesc smfilter =
child_start, /* child start */
NULL, /* child finish */
NULL, /* idle callback */
- milter_conn_accept /* Accept connection callback */
+ NULL /* Accept connection callback */
#endif
};
+
+/* Milter server functions */
+
+int
+milter_session_server(const char *id, int fd,
+ struct sockaddr const *sa, socklen_t len,
+ void *server_data, void *srvman_data)
+{
+ gacopyz_context_loop(fd, &smfilter, (milter_sockaddr_t*) sa, len,
+ (void*) id);
+ return 0;
+}
+
+void
+milter_setlogmask(int mask)
+{
+ smfilter.logmask = mask;
+}
+
+void
+milter_settimeout(time_t t)
+{
+ smfilter.ctx_timeout.tv_sec = t;
+ smfilter.ctx_timeout.tv_usec = 0;
+}
+
+
void
milter_enable_state(enum smtp_state state)
{
@@ -1001,59 +988,6 @@ check_pidfile(char *name)
}
}
-/* Check whether local socket P exists. Act in accordance with the settings
- of --remove command line option. */
-static void
-check_local_portspec(char *p)
-{
- struct stat st;
-
- if (stat(p, &st)) {
- if (errno == ENOENT)
- return; /* That's OK. The socket must not exist */
- mu_error(_("cannot stat file `%s': %s"),
- p, mu_strerror(errno));
- exit(EX_SOFTWARE);
- } else if (!force_remove) {
- mu_error(_("file `%s' already exists. Remove it or use --remove option."), p);
- exit(EX_SOFTWARE);
- }
-
- if (unlink (p)) {
- mu_error(_("cannot unlink file `%s': %s"), p, mu_strerror(errno));
- exit(EX_SOFTWARE);
- }
-}
-
-void
-check_portspec(char *portspec)
-{
- static struct spectab {
- char *proto;
- int skip;
- void (*check)(char *);
- } spectab[] = {
- { "/", 0, check_local_portspec },
- { "unix:", 1, check_local_portspec },
- { "local:", 1, check_local_portspec },
- { "inet:", 1, NULL },
- { "inet6:", 1, NULL },
- };
- struct spectab *sp;
-
- for (sp = spectab; sp < spectab + sizeof(spectab)/sizeof(spectab[0]);
- sp++) {
- int len = strlen(sp->proto);
- if (strlen(portspec) > len + 1
- && strncasecmp(portspec, sp->proto, len) == 0) {
- if (sp->check)
- sp->check(portspec + (sp->skip ? len : 0));
- return;
- }
- }
- mu_error(_("unknown port specification: %s"), portspec);
-}
-
static int x_argc; /* A copy of argc and ... */
static char **x_argv; /* ... argv */
static int restart; /* Set to 1 if restart is requested */
@@ -1086,21 +1020,19 @@ save_cmdline(int argc, char **argv)
static RETSIGTYPE
sig_stop(int sig)
{
- smfi_stop();
+ mfd_srvman_stop();
}
static RETSIGTYPE
sig_restart(int sig)
{
restart = 1;
- smfi_stop();
+ mfd_srvman_stop();
}
void
mailfromd_daemon()
{
- int rc;
-
mu_error(_("%s started"), program_version);
priv_setup();
@@ -1108,20 +1040,9 @@ mailfromd_daemon()
if (!foreground)
check_pidfile(pidfile);
- check_portspec(portspec);
-
- if (smfi_setconn(portspec) == MI_FAILURE) {
- mu_error("smfi_setconn: %s", mu_strerror(errno));
- exit(EX_SOFTWARE);
- }
-
- if (smfi_register(smfilter) == MI_FAILURE) {
- mu_error(_("smfi_register failed"));
- exit(EX_UNAVAILABLE);
- }
+ if (mfd_srvman_count_servers() == 0)
+ add_legacy_milter_port(DEFAULT_SOCKET, NULL);
- smfi_set_foreground(single_process_option);
-
signal(SIGTERM, sig_stop);
signal(SIGQUIT, sig_stop);
if (x_argc == 0) {
@@ -1150,15 +1071,15 @@ mailfromd_daemon()
}
umask(mailfromd_umask);
mu_daemon_create_pidfile(pidfile);
- rc = smfi_main();
} else {
umask(mailfromd_umask);
- rc = smfi_main();
}
- if (rc)
- mu_error(_("exit code = %d, last error status: %s"),
- rc, strerror (errno));
+ if (mfd_srvman_open())
+ exit(EX_UNAVAILABLE);
+ mfd_srvman_run(NULL);
+ mfd_srvman_shutdown();
+ mfd_srvman_free();
if (restart) {
char *s;
int i;
@@ -1174,5 +1095,5 @@ mailfromd_daemon()
mu_error(_("command line was: %s"), s);
} else
mu_error(_("mailfromd terminating"));
- exit(rc);
+ exit(0);
}
diff --git a/mfd/mailfromd.h b/mfd/mailfromd.h
index ce9f6a22..4ea27ff1 100644
--- a/mfd/mailfromd.h
+++ b/mfd/mailfromd.h
@@ -216,7 +216,6 @@ extern size_t variable_count;
extern int ignore_failed_reads_option;
extern int source_info_option;
extern int do_transcript;
-extern char *portspec;
extern unsigned long source_address;
extern char *pidfile;
extern time_t smtp_timeout_soft[], smtp_timeout_hard[];
@@ -232,7 +231,6 @@ extern char *mailfromd_state_dir;
extern int stack_trace_option;
extern int log_to_stderr;
extern const char *program_version;
-extern mu_acl_t mailfromd_acl;
extern mode_t mailfromd_umask;
extern size_t stack_size;
@@ -640,6 +638,7 @@ struct constant {
char *regex_flags_to_string(int flags, char *buf, size_t size);
+int add_legacy_milter_port(const char *str, mu_debug_t dbg);
void log_setup(int want_stderr);
void builtin_setup(void);
void builtin_post_setup(void);
@@ -820,6 +819,7 @@ void lex_drain_input(void);
int yyparse();
int yylex();
int yyerror(char *s);
+void free_parser_data(void);
void add_include_dir(const char *dir);
void tie_in_onblock(int enable);
int parse_program(char *name, int ydebug);
@@ -888,6 +888,7 @@ void set_milter_client_address(eval_environ_t env, milter_sockaddr_t *addr,
socklen_t len);
void set_milter_server_address(eval_environ_t env, milter_sockaddr_t *addr,
socklen_t len);
+void set_milter_server_id(eval_environ_t env, const char *id);
void set_cache_used(eval_environ_t env, int value);
@@ -973,6 +974,13 @@ void mailfromd_run(prog_counter_t entry, int argc, char **argv);
int set_option(char *name, char *value, int override);
void milter_enable_state(enum smtp_state state);
+int milter_session_server(const char *id,
+ int fd,
+ struct sockaddr const *sa, socklen_t len,
+ void *server_data, void *srvman_data);
+void milter_setlogmask(int mask);
+void milter_settimeout(time_t t);
+
int xeval(eval_environ_t env, enum smtp_state tag);
mf_status listens_on (const char *client_addr, int port, time_t timeout);
@@ -988,6 +996,7 @@ int relayed_domain_p(char *name);
int compare_string(const void *item, const void *value);
size_t mem_search(const char *str, int c, size_t size);
+
typedef struct mf_stack *mf_stack_t;
mf_stack_t mf_stack_create(size_t elsize, size_t count);
diff --git a/mfd/main.c b/mfd/main.c
index 1497a8c0..a1e89d6f 100644
--- a/mfd/main.c
+++ b/mfd/main.c
@@ -38,7 +38,10 @@
#include <mailutils/server.h>
#include <mailutils/syslog.h>
#include <mailutils/libargp.h>
+
#include "mailfromd.h"
+#include "srvman.h"
+#include "inttostr.h"
/* Configurable options */
@@ -67,9 +70,7 @@ int do_trace; /* Enable tracing configuration */
int mtasim_option; /* mtasim compatibility mode */
unsigned optimization_level = 1; /* Optimization level */
int log_to_stderr; /* Use stderr for logging */
-char *portspec; /* Communication socket specification.
- See init_names() */
-int force_remove; /* Remove local communication socket if it already
+int force_remove = 1; /* Remove local communication socket if it already
exists */
int foreground; /* Stay in foreground */
int single_process_option; /* Run in single process mode. */
@@ -135,9 +136,6 @@ time_t smtp_timeout_hard[SMTP_NUM_TIMEOUT] = {
/* I/O timeout. Overrides unset smtp_timeouts */
time_t io_timeout = 3;
-/* Access control list */
-mu_acl_t mailfromd_acl;
-
/* Umask for creating new files */
mode_t mailfromd_umask = 0017;
@@ -517,6 +515,122 @@ mf_option_source_ip(mu_debug_t err, const char *arg, unsigned long *pval)
}
+static char *
+state_dir_fixup(char *name)
+{
+ if (name[0] != '/') {
+ size_t slen = strlen(mailfromd_state_dir);
+ size_t olen = strlen(name);
+ size_t flen = slen + 1 + olen + 1;
+ char *p = xrealloc(name, flen);
+ memmove(p + slen + 1, p, olen + 1);
+ memcpy(p, mailfromd_state_dir, slen);
+ p[slen] = '/';
+ name = p;
+ }
+ return name;
+}
+
+
+static mu_url_t
+_parse_url(mu_debug_t err, const char *str)
+{
+ mu_url_t url;
+ int rc;
+ const char *s;
+
+ rc = mu_url_create(&url, str);
+ if (rc) {
+ if (err)
+ mf_error_on_locus(err,
+ _("cannot create URL from `%s': %s"),
+ str, mu_strerror(rc));
+ return NULL;
+ }
+ rc = mu_url_parse(url);
+ if (rc) {
+ mu_url_destroy(&url);
+ if (err)
+ mf_error_on_locus(err,
+ _("%s: error parsing URL: %s"),
+ str, mu_strerror(rc));
+ return NULL;
+ }
+
+ if (mu_url_sget_scheme(url, &s) == 0 && strcmp(s, "file") == 0)
+ mu_url_set_scheme(url, "unix");
+
+ return url;
+}
+
+static mu_url_t
+parse_milter_url(mu_debug_t err, const char *str)
+{
+ mu_url_t url;
+
+ url = _parse_url(NULL, str);
+ if (!url) {
+ char *tmp;
+ char *proto, *port, *path;
+
+ if (gacopyz_parse_connection(str,
+ &proto,
+ &port, &path) != MI_SUCCESS) {
+ mf_error_on_locus(err,
+ _("%s: error parsing URL"),
+ str);
+ }
+ if ((!proto
+ || strcmp(proto, "unix") == 0
+ || strcmp(proto, "local") == 0)
+ && !port
+ && path[0] != '/')
+ path = state_dir_fixup(path);
+ if (port)
+ asprintf(&tmp, "%s://%s:%s",
+ proto ? proto : "unix", path, port);
+ else
+ asprintf(&tmp, "%s://%s",
+ proto ? proto : "unix", path);
+ free(proto);
+ free(path);
+ free(port);
+ url = _parse_url(err, tmp);
+ free(tmp);
+ }
+ return url;
+}
+
+static const char *
+next_server_id()
+{
+ static size_t count;
+ static char nbuf[INT_BUFSIZE_BOUND(uintmax_t)];
+ count++;
+ return umaxtostr(count, nbuf);
+}
+
+int
+add_legacy_milter_port(const char *str, mu_debug_t dbg)
+{
+ mu_url_t url;
+ const char *id = next_server_id();
+
+ if (!dbg)
+ mu_diag_get_debug(&dbg);
+
+ url = parse_milter_url(dbg, str);
+ if (url) {
+ mfd_server_t srv = mfd_server_new(id, url,
+ milter_session_server);
+ if (srv)
+ mfd_srvman_attach_server(srv);
+ else
+ return 1; /* FIXME: Error message */
+ }
+ return 0;
+}
+
/* Option handling.
@@ -549,7 +663,7 @@ set_expire(void *value)
static void
set_port(void *value)
{
- portspec = value;
+ add_legacy_milter_port((const char*)value, NULL);
}
static void
@@ -1267,6 +1381,7 @@ parse_opt(int key, char *arg, struct argp_state *state)
break;
case 'r':
+ /*FIXME*/
force_remove = 1;
break;
@@ -1405,7 +1520,7 @@ parse_opt(int key, char *arg, struct argp_state *state)
argp_error(state,
_("%s: invalid log level"),
arg);
- smfi_setlogmask(SMI_LOG_FROM(lev));
+ milter_setlogmask(SMI_LOG_FROM(lev));
break;
}
@@ -1483,7 +1598,8 @@ parse_opt(int key, char *arg, struct argp_state *state)
break;
case ARGP_KEY_INIT:
- smfi_setlogmask(SMI_LOG_FROM(SMI_LOG_WARN));
+ milter_setlogmask(SMI_LOG_FROM(SMI_LOG_WARN));
+ milter_settimeout(7200);
break;
case ARGP_KEY_FINI:
@@ -1976,6 +2092,7 @@ smtp_timeout_cfg_init()
}
+
static int
cb_url(mu_debug_t err, void *data, mu_config_value_t *arg)
{
@@ -2037,6 +2154,102 @@ callout_resolver_cfg_init()
}
+/* Milter configuration */
+struct milter_config_stmt {
+ const char *id;
+ mu_url_t url;
+ mu_acl_t acl;
+ size_t max_children;
+};
+
+static struct milter_config_stmt milter_config_stmt;
+
+static int
+cb_milter_stmt_listen(mu_debug_t err, void *data, mu_config_value_t *arg)
+{
+ if (mu_cfg_assert_value_type(arg, MU_CFG_STRING, err))
+ return 1;
+ *(mu_url_t*)data = parse_milter_url(err, arg->v.string);
+ return 0;
+}
+
+struct mu_cfg_param milter_section_param[] = {
+ { "id", mu_cfg_string,
+ &milter_config_stmt.id, 0,
+ NULL,
+ N_("Server ID.") },
+ { "listen", mu_cfg_callback,
+ &milter_config_stmt.url, 0,
+ cb_milter_stmt_listen,
+ N_("Listen on this URL."),
+ N_("url") },
+ { "max-instances", mu_cfg_size,
+ &milter_config_stmt.max_children, 0,
+ NULL,
+ N_("Maximum number of instances allowed for this server.") },
+ { "acl", mu_cfg_section,
+ &milter_config_stmt.acl },
+ { NULL }
+};
+
+static int
+milter_section_parser(enum mu_cfg_section_stage stage,
+ const mu_cfg_node_t *node,
+ const char *section_label, void **section_data,
+ void *call_data,
+ mu_cfg_tree_t *tree)
+{
+ switch (stage) {
+ case mu_cfg_section_start:
+ memset(&milter_config_stmt, 0, sizeof(milter_config_stmt));
+ break;
+
+ case mu_cfg_section_end:
+ if (!milter_config_stmt.id)
+ milter_config_stmt.id = next_server_id();
+ if (milter_config_stmt.url) {
+ mfd_server_t srv =
+ mfd_server_new(milter_config_stmt.id,
+ milter_config_stmt.url,
+ milter_session_server);
+ if (srv) {
+ mfd_server_set_max_children(srv,
+ milter_config_stmt.max_children);
+ mfd_server_set_acl(srv,
+ milter_config_stmt.acl);
+ mfd_srvman_attach_server(srv);
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+void
+milter_cfg_init()
+{
+ struct mu_cfg_section *section;
+
+ if (mu_create_canned_section ("milter", &section) == 0) {
+ section->parser = milter_section_parser;
+ section->docstring = N_("Configure milter server.");
+ section->label = NULL;
+ mu_cfg_section_add_params(section, milter_section_param);
+ }
+}
+
+
+
+static int
+cb_milter_listen(mu_debug_t err, void *data, mu_config_value_t *arg)
+{
+ if (mu_cfg_assert_value_type(arg, MU_CFG_STRING, err))
+ return 1;
+ add_legacy_milter_port(arg->v.string, err);
+ return 0;
+}
+
+
struct mu_cfg_param mf_cfg_param[] = {
{ "debug", mu_cfg_callback, NULL, 0, cb_debug,
N_("Set Mailfromd debug verbosity level. Argument is a comma-"
@@ -2101,10 +2314,11 @@ struct mu_cfg_param mf_cfg_param[] = {
{ "state-directory", mu_cfg_callback, NULL, 0, cb_state_directory,
N_("Set program state directory."),
N_("dir") },
- { "listen", mu_cfg_string, &portspec, 0, NULL,
+ { "listen", mu_cfg_callback, NULL, 0, cb_milter_listen,
N_("Listen for milter requests on the given URL."),
N_("url") },
- { "acl", mu_cfg_section, &mailfromd_acl },
+ { "milter", mu_cfg_section },
+ { "acl", mu_cfg_section, &srvman_param.acl },
#ifdef USE_SYSLOG_ASYNC
{ "syslog-async", mu_cfg_bool, &use_syslog_async, 0, NULL,
N_("Use asynchronous syslog.") },
@@ -2192,7 +2406,6 @@ void
init_names()
{
mailfromd_state_dir = xstrdup(DEFAULT_STATE_DIR);
- portspec = xstrdup(DEFAULT_SOCKET);
pidfile = xstrdup(DEFAULT_PIDFILE);
}
@@ -2206,22 +2419,6 @@ db_format_setup()
greylist_format = db_format_install(greylist_format);
}
-static char *
-state_dir_fixup(char *name)
-{
- if (name[0] != '/') {
- size_t slen = strlen(mailfromd_state_dir);
- size_t olen = strlen(name);
- size_t flen = slen + 1 + olen + 1;
- char *p = xrealloc(name, flen);
- memmove(p + slen + 1, p, olen + 1);
- memcpy(p, mailfromd_state_dir, slen);
- p[slen] = '/';
- name = p;
- }
- return name;
-}
-
static int
db_fixup_name_enumerator(struct db_format *fmt, void *data)
{
@@ -2231,29 +2428,6 @@ db_fixup_name_enumerator(struct db_format *fmt, void *data)
}
static void
-portspec_fixup()
-{
- char *proto, *port, *path;
-
- if (gacopyz_parse_connection(portspec, &proto, &port, &path)
- != MI_SUCCESS)
- return; /* No need to complain: it is left to gacopyz connect
- routines */
-
- if ((!proto
- || strcmp(proto, "unix") == 0 || strcmp(proto, "local") == 0)
- && !port
- && path[0] != '/') {
- portspec = state_dir_fixup(path);
- path = NULL;
- }
-
- free(proto);
- free(port);
- free(path);
-}
-
-static void
fixup_state_dir_names()
{
size_t slen = strlen(mailfromd_state_dir);
@@ -2261,7 +2435,6 @@ fixup_state_dir_names()
mailfromd_state_dir[slen-1] = 0;
db_format_enumerate(db_fixup_name_enumerator, NULL);
pidfile = state_dir_fixup(pidfile);
- portspec_fixup();
}
void
@@ -2366,7 +2539,7 @@ mailfromd_show_defaults()
printf("preprocessor: %s\n", ext_pp ? ext_pp : "none");
printf("user: %s\n", user);
printf("statedir: %s\n", mailfromd_state_dir);
- printf("socket: %s\n", portspec);
+ printf("socket: %s\n", DEFAULT_SOCKET);
printf("pidfile: %s\n", pidfile);
#ifdef USE_SYSLOG_ASYNC
#if DEFAULT_SYSLOG_ASYNC == 1
@@ -2495,6 +2668,7 @@ main(int argc, char **argv)
mu_acl_cfg_init();
database_cfg_init();
smtp_timeout_cfg_init();
+ milter_cfg_init();
callout_resolver_cfg_init();
mu_argp_init(program_version, "<" PACKAGE_BUGREPORT ">");
diff --git a/mfd/srvman.c b/mfd/srvman.c
new file mode 100644
index 00000000..70c33ba5
--- /dev/null
+++ b/mfd/srvman.c
@@ -0,0 +1,810 @@
+/* This file is part of Mailfromd.
+ Copyright (C) 2005, 2006, 2007, 2008, 2009 Sergey Poznyakoff
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#define MF_SOURCE_NAME MF_SOURCE_SRVMAN
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <mailutils/mailutils.h>
+
+#include "mailfromd.h"
+#include "srvman.h"
+
+struct mfd_server {
+ struct mfd_server *prev; /* Link to the previous server */
+ struct mfd_server *next; /* Link to the next server */
+ char *id; /* Server ID */
+ struct sockaddr *sa; /* Socket address */
+ socklen_t salen; /* Length of the sa */
+ int backlog; /* Backlog value for listen(2) */
+ int fd; /* Socket descriptor */
+ mfd_server_prefork_hook_t prefork_hook; /* Pre-fork function */
+ mfd_server_func_t conn; /* Connection handler */
+ mfd_srvman_hook_t free_hook;
+ void *data; /* Server-specific data */
+ mu_acl_t acl; /* Access Control List */
+ size_t max_children; /* Maximum number of sub-processes to run. */
+ size_t num_children; /* Current number of running sub-processes. */
+ pid_t *pidtab; /* Array of child PIDs */
+ size_t pidtab_size; /* Number of elements in pidtab */
+};
+
+
+typedef RETSIGTYPE (*sig_handler_t) (int);
+
+union srvman_sockaddr {
+ struct sockaddr sa;
+ struct sockaddr_in s_in;
+ struct sockaddr_un s_un;
+};
+
+struct srvman {
+ struct mfd_server *head, *tail; /* List of servers */
+ size_t num_children; /* Current number of running
+ sub-processes. */
+ sigset_t sigmask; /* A set of signals to handle by the
+ manager. */
+ sig_handler_t sigtab[NSIG]; /* Keeps old signal handlers. */
+};
+
+struct srvman_param srvman_param;
+static struct srvman srvman;
+
+static sig_handler_t
+set_signal(int sig, sig_handler_t handler)
+{
+#ifdef HAVE_SIGACTION
+ struct sigaction act, oldact;
+ act.sa_handler = handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ sigaction(sig, &act, &oldact);
+ return oldact.sa_handler;
+#else
+ return signal(sig, handler);
+#endif
+}
+
+static int volatile need_cleanup;
+static int volatile stop;
+
+void
+mfd_srvman_stop()
+{
+ stop = 1;
+}
+
+static RETSIGTYPE
+srvman_signal (int signo)
+{
+ switch (signo) {
+ case SIGCHLD:
+ need_cleanup = 1;
+ break;
+
+ default:
+ /* FIXME: */
+ stop = 1;
+ break;
+ }
+#ifndef HAVE_SIGACTION
+ signal(signo, srvman_signal);
+#endif
+}
+
+static void
+set_signal_handlers()
+{
+ int i;
+
+ for (i = 0; i < NSIG; i++)
+ if (sigismember (&srvman.sigmask, i))
+ srvman.sigtab[i] = set_signal(i, srvman_signal);
+}
+
+static void
+restore_signal_handlers()
+{
+ int i;
+
+ for (i = 0; i < NSIG; i++)
+ if (sigismember (&srvman.sigmask, i))
+ set_signal(i, srvman.sigtab[i]);
+}
+
+static void
+close_fds(fd_set *exfd)
+{
+ int i;
+
+ for (i = getmaxfd(); i >= 0; i--)
+ if (!FD_ISSET(i, exfd))
+ close(i);
+}
+
+
+struct sockaddr *
+srvman_url_to_sockaddr(mu_url_t url, size_t *psalen)
+{
+ int rc;
+ const char *sval;