(asynchronous bounce client), qmgr/qmgr_active.c. Problem
reported by El Bunzo (webpower.nl) and Tiger Technologies
(tigertech.com).
+
+20001209
+
+ Feature: mailbox_transport and fallback_transport can now
+ have the form transport:nexthop, with suitable defaults
+ when either transport or nexthop are omitted, just like in
+ the Postfix transport map. This allows you to specify for
+ example, "mailbox_transport = lmtp:unix:/file/name". File:
+ global/deliver_pass.c.
+
+ Bugfix: local_destination_concurrency_limit no longer works
+ as per-user concurrency limit but as per-domain limit, so
+ the limit of "2" in the sample main.cf files would result
+ in poor local delivery performance. The Postfix install
+ procedure repairs this. Problem reported by David Schweikert
+ (ee.ethz.ch) and Dallas Wisehaupt (cynicism.com).
"command_directory = $command_directory" \
"queue_directory = $queue_directory" \
"mail_owner = $mail_owner" \
+ "local_destination_concurrency_limit = 0" \
|| exit 1
(echo "# This file was generated by $0"
#
# Each message delivery transport has its XXX_destination_concurrency_limit
# parameter. The default is $default_destination_concurrency_limit.
+# The limit is per destination domain, so the destination concurrency
+# limit for local delivery must be set to zero otherwise performance
+# will suffer.
-local_destination_concurrency_limit = 2
+local_destination_concurrency_limit = 0
default_destination_concurrency_limit = 10
# DEBUGGING CONTROL
# has precedence over the mailbox_command, fallback_transport and
# luser_relay parameters.
#
+# Specify a string of the form transport:nexthop, where transport is
+# the name of a mail delivery transport defined in master.cf. Either
+# transport or nexthop are optional. For more details see the sample
+# transports file.
+#
+# mailbox_transport = lmtp:unix:/file/name
+# mailbox_transport = cyrus
mailbox_transport =
# The fallback_transport specifies the optional transport in master.cf
# to use for recipients that are not found in the UNIX passwd database.
# This parameter has precedence over the luser_relay parameter.
#
+# Specify a string of the form transport:nexthop, where transport is
+# the name of a mail delivery transport defined in master.cf. Either
+# transport or nexthop are optional. For more details see the sample
+# transports file.
+#
+# fallback_transport = lmtp:unix:/file/name
+# fallback_transport = cyrus
fallback_transport =
#
#
# The local_destination_concurrency_limit parameter limits the number
-# of parallel deliveries to the same local recipient.
+# of parallel deliveries to the local domain. It should therefore be
+# set to zero, in order to prevent poor local delivery performance.
+# In old Postfix versions this parameter implemented a per-user
+# limit. That is no longer the case.
#
# The default limit is taken from the default_destination_concurrency_limit
-# parameter. I recommend a low limit of 2, just in case someone has
-# an expensive shell command in a .forward file or in an alias (e.g.,
-# a mailing list manager). You don't want to run lots of those.
+# parameter.
#
-local_destination_concurrency_limit = 2
+local_destination_concurrency_limit = 0
# The local_destination_recipient_limit parameter limits the number
# of recipients per local message delivery. The default limit is
{
static CONFIG_INT_TABLE int_table[] = {
VAR_BOUNCE_LIMIT, DEF_BOUNCE_LIMIT, &var_bounce_limit, 1, 0,
- VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 1, 0,
+ VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 1, 1000,
VAR_DELAY_WARN_TIME, DEF_DELAY_WARN_TIME, &var_delay_warn_time, 0, 0,
0,
};
/* DESCRIPTION
/* deliver_flock() sets one exclusive kernel lock on an open file,
/* for example in order to deliver mail.
-/* It performs several non-blocking attempts to acquire an exclusive
+/* It performs several non-blocking attempts to acquire an exclusive
/* lock before giving up.
/*
/* Arguments:
#include "mail_params.h"
#include "deliver_flock.h"
-/* deliver_flock - lock open file for mail delivery*/
+/* deliver_flock - lock open file for mail delivery */
-int deliver_flock(int fd, int lock_style, VSTRING * why)
+int deliver_flock(int fd, int lock_style, VSTRING *why)
{
int i;
/* .IP class
/* Destination delivery agent service class
/* .IP service
-/* Destination delivery agent service name.
+/* String of the form \fItransport\fR:\fInexthop\fR. Either transport
+/* or nexthop are optional. For details see the transport map manual page.
/* .IP request
/* Delivery request with queue file information.
/* .IP address
#include <msg.h>
#include <vstring.h>
#include <vstream.h>
+#include <split_at.h>
+#include <mymalloc.h>
/* Global library. */
+#include <mail_params.h>
#include <deliver_pass.h>
/* deliver_pass_initial_reply - retrieve initial delivery process response */
/* deliver_pass_send_request - send delivery request to delivery process */
static int deliver_pass_send_request(VSTREAM *stream, DELIVER_REQUEST *request,
- const char *addr, long offs)
+ const char *nexthop, const char *addr, long offs)
{
int stat;
request->flags,
request->queue_name, request->queue_id,
request->data_offset, request->data_size,
- request->nexthop, request->sender,
+ nexthop, request->sender,
request->errors_to, request->return_receipt,
request->arrival_time,
offs, addr, "0");
VSTREAM *stream;
VSTRING *reason;
int status;
+ char *saved_service;
+ char *transport;
+ char *nexthop;
+
+ /*
+ * Parse service into transport:nexthop form, and allow for omission of
+ * optional fields
+ */
+ transport = saved_service = mystrdup(service);
+ if ((nexthop = split_at(saved_service, ':')) == 0 || *nexthop == 0)
+ nexthop = request->nexthop;
+ if (*transport == 0)
+ transport = var_def_transport;
/*
* Initialize.
*/
- stream = mail_connect_wait(class, service);
+ stream = mail_connect_wait(class, transport);
reason = vstring_alloc(1);
/*
* different transport.
*/
if ((status = deliver_pass_initial_reply(stream)) == 0
- && (status = deliver_pass_send_request(stream, request, addr, offs)) == 0)
+ && (status = deliver_pass_send_request(stream, request, nexthop,
+ addr, offs)) == 0)
status = deliver_pass_final_reply(stream, reason);
/*
*/
vstream_fclose(stream);
vstring_free(reason);
+ myfree(saved_service);
return (status);
}
extern char *get_mail_conf_str(const char *, const char *, int, int);
extern int get_mail_conf_int(const char *, int, int, int);
extern int get_mail_conf_bool(const char *, int);
-extern int get_mail_conf_lock(const char *, int);
extern int get_mail_conf_time(const char *, const char *, int, int, int);
extern char *get_mail_conf_raw(const char *, const char *, int, int);
extern char *get_mail_conf_str_fn(const char *, const char *(*) (void), int, int);
extern int get_mail_conf_int_fn(const char *, int (*) (void), int, int);
extern int get_mail_conf_bool_fn(const char *, int (*) (void));
-extern int get_mail_conf_lock_fn(const char *, int (*) (void));
extern int get_mail_conf_time_fn(const char *, const char *(*) (void), int, int, int);
extern char *get_mail_conf_raw_fn(const char *, const char *(*) (void), int, int);
extern void set_mail_conf_str(const char *, const char *);
extern void set_mail_conf_int(const char *, int);
extern void set_mail_conf_bool(const char *, int);
-extern void set_mail_conf_lock(const char *, int);
extern void set_mail_conf_time(const char *, const char *);
/*
int *target; /* pointer to global variable */
} CONFIG_BOOL_TABLE;
-typedef struct {
- const char *name; /* config variable name */
- int defval; /* default value */
- int *target; /* pointer to global variable */
-} CONFIG_LOCK_TABLE;
-
typedef struct {
const char *name; /* config variable name */
const char *defval; /* default value */
extern void get_mail_conf_str_table(CONFIG_STR_TABLE *);
extern void get_mail_conf_int_table(CONFIG_INT_TABLE *);
extern void get_mail_conf_bool_table(CONFIG_BOOL_TABLE *);
-extern void get_mail_conf_lock_table(CONFIG_LOCK_TABLE *);
extern void get_mail_conf_time_table(CONFIG_TIME_TABLE *);
extern void get_mail_conf_raw_table(CONFIG_STR_TABLE *);
int *target; /* pointer to global variable */
} CONFIG_BOOL_FN_TABLE;
-typedef struct {
- const char *name; /* config variable name */
- int (*defval) (void); /* default value provider */
- int *target; /* pointer to global variable */
-} CONFIG_LOCK_FN_TABLE;
-
typedef struct {
const char *name; /* config variable name */
const char *(*defval) (void); /* default value provider */
extern void get_mail_conf_str_fn_table(CONFIG_STR_FN_TABLE *);
extern void get_mail_conf_int_fn_table(CONFIG_INT_FN_TABLE *);
extern void get_mail_conf_bool_fn_table(CONFIG_BOOL_FN_TABLE *);
-extern void get_mail_conf_lock_fn_table(CONFIG_LOCK_FN_TABLE *);
extern void get_mail_conf_time_fn_table(CONFIG_TIME_FN_TABLE *);
extern void get_mail_conf_raw_fn_table(CONFIG_STR_FN_TABLE *);
/* char *var_syslog_facility;
/* char *var_relay_domains;
/* char *var_fflush_domains;
+/* char *var_def_transport;
/*
/* char *var_import_environ;
/* char *var_export_environ;
#include "mynetworks.h"
#include "mail_conf.h"
#include "mail_version.h"
+#include "mail_proto.h"
#include "mail_params.h"
/*
char *var_syslog_facility;
char *var_relay_domains;
char *var_fflush_domains;
+char *var_def_transport;
char *var_import_environ;
char *var_export_environ;
VAR_FFLUSH_DOMAINS, DEF_FFLUSH_DOMAINS, &var_fflush_domains, 0, 0,
VAR_EXPORT_ENVIRON, DEF_EXPORT_ENVIRON, &var_export_environ, 0, 0,
VAR_IMPORT_ENVIRON, DEF_IMPORT_ENVIRON, &var_import_environ, 0, 0,
+ VAR_DEF_TRANSPORT, DEF_DEF_TRANSPORT, &var_def_transport, 0, 0,
0,
};
static CONFIG_STR_FN_TABLE function_str_defaults_2[] = {
/* LICENSE
/* .ad
/* .fi
-Unterminated comment
-
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/*
* Treat fork() failure as a transient problem. Treat bad handshake as a
* permanent error.
+ *
+ * XXX Are we invoking a Postfix process or a non-Postfix process? In the
+ * former case we can share the full environment; in the latter case only
+ * a restricted environment should be propagated. Even though we are
+ * talking a Postfix-internal protocol there is no way we can tell what
+ * is being executed except by duplicating a lot of existing code.
*/
export_env = argv_split(var_export_environ, ", \t\r\n");
while ((stream = vstream_popen(O_RDWR,
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-20001208"
+#define DEF_MAIL_VERSION "Snapshot-20001209"
extern char *var_mail_version;
/* LICENSE
/* ARGV *mbox_lock_names()
/* DESCRIPTION
/* The functions in this module translate between external
-/* mailbox locking method names and internal forms.
+/* mailbox locking method names and internal representations.
/*
/* mbox_lock_mask() translates a string with locking method names
/* into a bit mask. Names are separated by comma or whitespace.
/* .IP "flock (MBOX_FLOCK_LOCK)"
/* Use flock() style lock after opening the file. This is the mailbox
/* locking method traditionally used on BSD-ish systems (including
-/* Ultrix and SunOS).
+/* Ultrix and SunOS). It is not suitable for remote file systems.
/* .IP "fcntl (MBOX_FCNTL_LOCK)"
/* Use fcntl() style lock after opening the file. This is the mailbox
/* locking method on System-V-ish systems (Solaris, AIX, IRIX, HP-UX).
+/* This method is supposed to work for remote systems, but often
+/* has problems.
/* .IP "dotlock (MBOX_DOT_LOCK)"
/* Create a lock file with the name \fIfilename\fB.lock\fR. This
-/* method pre-dates kernel locks.
+/* method pre-dates kernel locks. This works with remote file systems,
+/* modulo cache coherency problems.
/* .PP
/* mbox_lock_names() returns an array with the names of available
/* mailbox locking methods. The result should be given to argv_free().
/*
* The table with available mailbox locking methods. Some systems have
- * flock() locks; all POSIX-compatible systems should have fcntl() locks.
- * Even though some systems do not use dotlock files, it can be necessary
- * when accessing mailbox files over NFS.
+ * flock() locks; all POSIX-compatible systems have fcntl() locks. Even
+ * though some systems do not use dotlock files by default (4.4BSD), such
+ * locks can be necessary when accessing mailbox files over NFS.
*/
static NAME_MASK mbox_mask[] = {
#ifdef HAS_FLOCK_LOCK
/* lmtp_service - perform service for client */
-static void lmtp_service(VSTREAM * client_stream, char *unused_service, char **argv)
+static void lmtp_service(VSTREAM *client_stream, char *unused_service, char **argv)
{
DELIVER_REQUEST *request;
int status;
local_mbox_lock_mask &= MBOX_DOT_LOCK;
}
if (local_mbox_lock_mask == 0)
- msg_fatal("no applicable mailbox locking method");
+ msg_fatal("parameter %s specifies no applicable mailbox locking method",
+ VAR_MAILBOX_LOCK);
}
/* pre_accept - see if tables have changed */
/* multi_server_disconnect - terminate client session */
-void multi_server_disconnect(VSTREAM * stream)
+void multi_server_disconnect(VSTREAM *stream)
{
if (msg_verbose)
msg_info("connection closed fd %d", vstream_fileno(stream));
".", service_name, (char *) 0);
why = vstring_alloc(1);
if ((multi_server_lock = safe_open(lock_path, O_CREAT | O_RDWR, 0600,
- (struct stat *) 0, -1, -1, why)) == 0)
+ (struct stat *) 0, -1, -1, why)) == 0)
msg_fatal("open lock file %s: %s", lock_path, vstring_str(why));
close_on_exec(vstream_fileno(multi_server_lock), CLOSE_ON_EXEC);
myfree(lock_path);
".", service_name, (char *) 0);
why = vstring_alloc(1);
if ((single_server_lock = safe_open(lock_path, O_CREAT | O_RDWR, 0600,
- (struct stat *) 0, -1, -1, why)) == 0)
+ (struct stat *) 0, -1, -1, why)) == 0)
msg_fatal("open lock file %s: %s", lock_path, vstring_str(why));
close_on_exec(vstream_fileno(single_server_lock), CLOSE_ON_EXEC);
myfree(lock_path);
".", service_name, (char *) 0);
why = vstring_alloc(1);
if ((trigger_server_lock = safe_open(lock_path, O_CREAT | O_RDWR, 0600,
- (struct stat *) 0, -1, -1, why)) == 0)
+ (struct stat *) 0, -1, -1, why)) == 0)
msg_fatal("open lock file %s: %s", lock_path, vstring_str(why));
close_on_exec(vstream_fileno(trigger_server_lock), CLOSE_ON_EXEC);
myfree(lock_path);
VAR_MAX_BACKOFF_TIME, DEF_MAX_BACKOFF_TIME, &var_max_backoff_time, 1, 0,
VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 1, 1000,
VAR_QMGR_ACT_LIMIT, DEF_QMGR_ACT_LIMIT, &var_qmgr_active_limit, 1, 0,
- VAR_QMGR_RCPT_LIMIT, DEF_QMGR_RCPT_LIMIT, &var_qmgr_rcpt_limit, 0, 0,
+ VAR_QMGR_RCPT_LIMIT, DEF_QMGR_RCPT_LIMIT, &var_qmgr_rcpt_limit, 1, 0,
VAR_QMGR_MSG_RCPT_LIMIT, DEF_QMGR_MSG_RCPT_LIMIT, &var_qmgr_msg_rcpt_limit, 1, 0,
VAR_XPORT_RCPT_LIMIT, DEF_XPORT_RCPT_LIMIT, &var_xport_rcpt_limit, 0, 0,
VAR_STACK_RCPT_LIMIT, DEF_STACK_RCPT_LIMIT, &var_stack_rcpt_limit, 0, 0,
&& (close(fd), open("/dev/null", O_RDWR, 0)) != fd)
msg_fatal("open /dev/null: %m");
- /*
- * Strip the environment so we don't have to trust the C library.
- */
- import_env = argv_split(var_import_environ, ", \t\r\n");
- clean_env(import_env->argv);
- argv_free(import_env);
-
/*
* Set up logging. Censor the process name: it is provided by the user.
*/
* perform some sanity checks on the input.
*/
mail_conf_read();
+
+ /*
+ * Strip the environment so we don't have to trust the C library.
+ */
+ import_env = argv_split(var_import_environ, ", \t\r\n");
+ clean_env(import_env->argv);
+ argv_free(import_env);
+
if (chdir(var_queue_dir))
msg_fatal("chdir %s: %m", var_queue_dir);
if (msg_verbose)
* Tunable parameters.
*/
char *var_transport_maps;
-char *var_def_transport;
bool var_swap_bangpath;
bool var_append_dot_mydomain;
bool var_append_at_myorigin;
{
static CONFIG_STR_TABLE str_table[] = {
VAR_TRANSPORT_MAPS, DEF_TRANSPORT_MAPS, &var_transport_maps, 0, 0,
- VAR_DEF_TRANSPORT, DEF_DEF_TRANSPORT, &var_def_transport, 0, 0,
VAR_LOCAL_TRANSPORT, DEF_LOCAL_TRANSPORT, &var_local_transport, 0, 0,
0,
};
if (fstat_st == 0)
fstat_st = &local_statbuf;
if (fstat(vstream_fileno(fp), fstat_st) < 0) {
- msg_fatal("bad open file status: %m");
- } else if (fstat_st->st_nlink != 1) {
+ msg_fatal("%s: bad open file status: %m", path);
+ } else if (fstat_st->st_nlink > 1) {
vstring_sprintf(why, "file has multiple hard links");
} else if (S_ISDIR(fstat_st->st_mode)) {
vstring_sprintf(why, "file is a directory");
* Optionally look up the file attributes.
*/
if (st != 0 && fstat(vstream_fileno(fp), st) < 0)
- msg_fatal("bad open file status: %m");
+ msg_fatal("%s: bad open file status: %m", path);
/*
* We are almost there...