diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-11-21 01:46:47 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-11-21 01:46:47 +0200 |
commit | 118f9d898f8e0208aa01275cf37752b888c39d20 (patch) | |
tree | 93c29187b3a8db6e71b12da2f45e96c38cde2baa | |
parent | c9f9835a1820fa85ae49f2f511ce793255618bcb (diff) | |
download | mailfromd-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.c | 8 | ||||
-rw-r--r-- | gacopyz/gacopyz.c | 11 | ||||
-rw-r--r-- | gacopyz/gacopyz.h | 7 | ||||
-rw-r--r-- | gacopyz/gacopyz_priv.h | 3 | ||||
-rw-r--r-- | mfd/Makefile.am | 2 | ||||
-rw-r--r-- | mfd/bi_vars.m4 | 9 | ||||
-rw-r--r-- | mfd/engine.c | 161 | ||||
-rw-r--r-- | mfd/mailfromd.h | 13 | ||||
-rw-r--r-- | mfd/main.c | 280 | ||||
-rw-r--r-- | mfd/srvman.c | 810 | ||||
-rw-r--r-- | mfd/srvman.h | 68 | ||||
-rw-r--r-- | mflib/Makefile.am | 7 |
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); @@ -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", §ion) == 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; |