Cleanup: removed redundant sort operation. Viktor Dukhovni.
File: tls/tls_dane.c.
+
+20131119
+
+ Bugfix (introduced: 20111211): the Postfix memcache client
+ did not propagate a persistent "open()" lock to the optional
+ backup database. File: global/dict_memcache.c.
+
+ Feature: a Postfix LMDB database can now be used as shared
+ cache. Until now only the Postfix memcache database could
+ be used in this manner. This is implemented by allowing a
+ database to downgrade the permanent DICT_FLAG_OPEN_LOCK
+ method to the temporary DICT_FLAG_LOCK method. Files:
+ util/dict.h, util/dict_alloc.c, util/dict_open.c,
+ util/dict_lmdb.c.
+
+ Internal: DNS client support to report reply RCODE information,
+ in addition to the simplified DNS_NOTFOUND, DNS_RETRY etc.
+ Files: dns/dns.h. dns/dns_lookup.c, dns/test_dns_lookup.c.
ldap_table - Postfix LDAP client configuration
<b>SYNOPSIS</b>
- <b>postmap -q "<i></b>string</i><b>" <a href="ldap_table.5.html">ldap</a>:/etc/postfix/filename</b>
+ <b>postmap -q "</b><i>string</i><b>" <a href="ldap_table.5.html">ldap</a>:/etc/postfix/</b><i>filename</i>
- <b>postmap -q - <a href="ldap_table.5.html">ldap</a>:/etc/postfix/<i></b>filename</i> <<i>inputfile</i>
+ <b>postmap -q - <a href="ldap_table.5.html">ldap</a>:/etc/postfix/</b><i>filename</i> <<i>inputfile</i>
<b>DESCRIPTION</b>
The Postfix mail system uses optional tables for address
memcache_table - Postfix memcache client configuration
<b>SYNOPSIS</b>
- <b>postmap -q "</b><i>string</i><b>" <a href="memcache_table.5.html">memcache</a>:/etc/postfix/filename</b>
+ <b>postmap -q "</b><i>string</i><b>" <a href="memcache_table.5.html">memcache</a>:/etc/postfix/</b><i>filename</i>
<b>postmap -q - <a href="memcache_table.5.html">memcache</a>:/etc/postfix/</b><i>filename</i> <<i>inputfile</i>
mysql_table - Postfix MySQL client configuration
<b>SYNOPSIS</b>
- <b>postmap -q "</b><i>string</i><b>" <a href="mysql_table.5.html">mysql</a>:/etc/postfix/filename</b>
+ <b>postmap -q "</b><i>string</i><b>" <a href="mysql_table.5.html">mysql</a>:/etc/postfix/</b><i>filename</i>
<b>postmap -q - <a href="mysql_table.5.html">mysql</a>:/etc/postfix/</b><i>filename</i> <<i>inputfile</i>
pgsql_table - Postfix PostgreSQL client configuration
<b>SYNOPSIS</b>
- <b>postmap -q "</b><i>string</i><b>" <a href="pgsql_table.5.html">pgsql</a>:/etc/postfix/filename</b>
+ <b>postmap -q "</b><i>string</i><b>" <a href="pgsql_table.5.html">pgsql</a>:/etc/postfix/</b><i>filename</i>
<b>postmap -q - <a href="pgsql_table.5.html">pgsql</a>:/etc/postfix/</b><i>filename</i> <<i>inputfile</i>
extension by the first character that matches the <a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a>
set. </p>
-<p> When used in <a href="postconf.5.html#forward_path">forward_path</a>, ${<a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a>} is replaced
+<p> When used in <a href="postconf.5.html#command_execution_directory">command_execution_directory</a>, <a href="postconf.5.html#forward_path">forward_path</a>, or
+<a href="postconf.5.html#luser_relay">luser_relay</a>, ${<a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a>} is replaced
with the recipient delimiter that was found in the recipient email
address (Postfix 2.11 and later), or it is replaced with the <a href="postconf.5.html">main.cf</a>
<a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> parameter value (Postfix 2.10 and earlier).
sqlite_table - Postfix SQLite configuration
<b>SYNOPSIS</b>
- <b>postmap -q "</b><i>string</i><b>" <a href="sqlite_table.5.html">sqlite</a>:/etc/postfix/filename</b>
+ <b>postmap -q "</b><i>string</i><b>" <a href="sqlite_table.5.html">sqlite</a>:/etc/postfix/</b><i>filename</i>
<b>postmap -q - <a href="sqlite_table.5.html">sqlite</a>:/etc/postfix/</b><i>filename</i> <<i>inputfile</i>
.nf
\fBpostmap -q "\fIstring\fB" cidr:/etc/postfix/\fIfilename\fR
-\fBpostmap -q - cidr:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
+\fBpostmap -q - cidr:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
.SH DESCRIPTION
.ad
.fi
.SH "SYNOPSIS"
.na
.nf
-\fBpostmap -q "\fIstring\fB" ldap:/etc/postfix/filename\fR
+\fBpostmap -q "\fIstring\fB" ldap:/etc/postfix/\fIfilename\fR
-\fBpostmap -q - ldap:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
+\fBpostmap -q - ldap:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
.SH DESCRIPTION
.ad
.fi
.SH "SYNOPSIS"
.na
.nf
-\fBpostmap -q "\fIstring\fB" memcache:/etc/postfix/filename\fR
+\fBpostmap -q "\fIstring\fB" memcache:/etc/postfix/\fIfilename\fR
-\fBpostmap -q - memcache:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
+\fBpostmap -q - memcache:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
.SH DESCRIPTION
.ad
.fi
.SH "SYNOPSIS"
.na
.nf
-\fBpostmap -q "\fIstring\fB" mysql:/etc/postfix/filename\fR
+\fBpostmap -q "\fIstring\fB" mysql:/etc/postfix/\fIfilename\fR
-\fBpostmap -q - mysql:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
+\fBpostmap -q - mysql:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
.SH DESCRIPTION
.ad
.fi
.nf
\fBpostmap -q "\fIstring\fB" "nisplus:[\fIname\fB=%s];\fIname.name.\fB"\fR
-\fBpostmap -q - "nisplus:[\fIname\fB=%s];\fIname.name.\fB"\fR <\fIinputfile\fR
+\fBpostmap -q - "nisplus:[\fIname\fB=%s];\fIname.name.\fB" <\fIinputfile\fR
.SH DESCRIPTION
.ad
.fi
.nf
\fBpostmap -q "\fIstring\fB" pcre:/etc/postfix/\fIfilename\fR
-\fBpostmap -q - pcre:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
+\fBpostmap -q - pcre:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
.SH DESCRIPTION
.ad
.fi
.SH "SYNOPSIS"
.na
.nf
-\fBpostmap -q "\fIstring\fB" pgsql:/etc/postfix/filename\fR
+\fBpostmap -q "\fIstring\fB" pgsql:/etc/postfix/\fIfilename\fR
-\fBpostmap -q - pgsql:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
+\fBpostmap -q - pgsql:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
.SH DESCRIPTION
.ad
.fi
extension by the first character that matches the recipient_delimiter
set.
.PP
-When used in forward_path, ${recipient_delimiter} is replaced
+When used in command_execution_directory, forward_path, or
+luser_relay, ${recipient_delimiter} is replaced
with the recipient delimiter that was found in the recipient email
address (Postfix 2.11 and later), or it is replaced with the main.cf
recipient_delimiter parameter value (Postfix 2.10 and earlier).
.nf
\fBpostmap -q "\fIstring\fB" regexp:/etc/postfix/\fIfilename\fR
-\fBpostmap -q - regexp:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
+\fBpostmap -q - regexp:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
.SH DESCRIPTION
.ad
.fi
.SH "SYNOPSIS"
.na
.nf
-\fBpostmap -q "\fIstring\fB" sqlite:/etc/postfix/filename\fR
+\fBpostmap -q "\fIstring\fB" sqlite:/etc/postfix/\fIfilename\fR
-\fBpostmap -q - sqlite:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
+\fBpostmap -q - sqlite:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
.SH DESCRIPTION
.ad
.fi
.nf
\fBpostmap -q "\fIstring\fB" tcp:\fIhost:port\fR
-\fBpostmap -q - tcp:\fIhost:port\fR <\fIinputfile\fR
+\fBpostmap -q - tcp:\fIhost:port\fB <\fIinputfile\fR
.SH DESCRIPTION
.ad
.fi
# SYNOPSIS
# \fBpostmap -q "\fIstring\fB" cidr:/etc/postfix/\fIfilename\fR
#
-# \fBpostmap -q - cidr:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
+# \fBpostmap -q - cidr:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
# DESCRIPTION
# The Postfix mail system uses optional lookup tables.
# These tables are usually in \fBdbm\fR or \fBdb\fR format.
# SUMMARY
# Postfix LDAP client configuration
# SYNOPSIS
-# \fBpostmap -q "\fIstring\fB" ldap:/etc/postfix/filename\fR
+# \fBpostmap -q "\fIstring\fB" ldap:/etc/postfix/\fIfilename\fR
#
-# \fBpostmap -q - ldap:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
+# \fBpostmap -q - ldap:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
# DESCRIPTION
# The Postfix mail system uses optional tables for address
# rewriting or mail routing. These tables are usually in
# SUMMARY
# Postfix memcache client configuration
# SYNOPSIS
-# \fBpostmap -q "\fIstring\fB" memcache:/etc/postfix/filename\fR
+# \fBpostmap -q "\fIstring\fB" memcache:/etc/postfix/\fIfilename\fR
#
-# \fBpostmap -q - memcache:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
+# \fBpostmap -q - memcache:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
# DESCRIPTION
# The Postfix mail system uses optional tables for address
# rewriting or mail routing. These tables are usually in
# SUMMARY
# Postfix MySQL client configuration
# SYNOPSIS
-# \fBpostmap -q "\fIstring\fB" mysql:/etc/postfix/filename\fR
+# \fBpostmap -q "\fIstring\fB" mysql:/etc/postfix/\fIfilename\fR
#
-# \fBpostmap -q - mysql:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
+# \fBpostmap -q - mysql:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
# DESCRIPTION
# The Postfix mail system uses optional tables for address
# rewriting or mail routing. These tables are usually in
# SYNOPSIS
# \fBpostmap -q "\fIstring\fB" "nisplus:[\fIname\fB=%s];\fIname.name.\fB"\fR
#
-# \fBpostmap -q - "nisplus:[\fIname\fB=%s];\fIname.name.\fB"\fR <\fIinputfile\fR
+# \fBpostmap -q - "nisplus:[\fIname\fB=%s];\fIname.name.\fB" <\fIinputfile\fR
# DESCRIPTION
# The Postfix mail system uses optional lookup tables.
# These tables are usually in \fBdbm\fR or \fBdb\fR format.
# SYNOPSIS
# \fBpostmap -q "\fIstring\fB" pcre:/etc/postfix/\fIfilename\fR
#
-# \fBpostmap -q - pcre:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
+# \fBpostmap -q - pcre:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
# DESCRIPTION
# The Postfix mail system uses optional tables for address
# rewriting, mail routing, or access control. These tables
# SUMMARY
# Postfix PostgreSQL client configuration
# SYNOPSIS
-# \fBpostmap -q "\fIstring\fB" pgsql:/etc/postfix/filename\fR
+# \fBpostmap -q "\fIstring\fB" pgsql:/etc/postfix/\fIfilename\fR
#
-# \fBpostmap -q - pgsql:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
+# \fBpostmap -q - pgsql:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
# DESCRIPTION
# The Postfix mail system uses optional tables for address
# rewriting or mail routing. These tables are usually in
# SYNOPSIS
# \fBpostmap -q "\fIstring\fB" regexp:/etc/postfix/\fIfilename\fR
#
-# \fBpostmap -q - regexp:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
+# \fBpostmap -q - regexp:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
# DESCRIPTION
# The Postfix mail system uses optional tables for address
# rewriting, mail routing, or access control. These tables
# SUMMARY
# Postfix SQLite configuration
# SYNOPSIS
-# \fBpostmap -q "\fIstring\fB" sqlite:/etc/postfix/filename\fR
+# \fBpostmap -q "\fIstring\fB" sqlite:/etc/postfix/\fIfilename\fR
#
-# \fBpostmap -q - sqlite:/etc/postfix/\fIfilename\fR <\fIinputfile\fR
+# \fBpostmap -q - sqlite:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
# DESCRIPTION
# The Postfix mail system uses optional tables for address
# rewriting or mail routing. These tables are usually in
# SYNOPSIS
# \fBpostmap -q "\fIstring\fB" tcp:\fIhost:port\fR
#
-# \fBpostmap -q - tcp:\fIhost:port\fR <\fIinputfile\fR
+# \fBpostmap -q - tcp:\fIhost:port\fB <\fIinputfile\fR
# DESCRIPTION
# The Postfix mail system uses optional tables for address
# rewriting or mail routing. These tables are usually in
/*
* dns_lookup.c
*/
-extern int dns_lookup(const char *, unsigned, unsigned, DNS_RR **,
- VSTRING *, VSTRING *);
-extern int dns_lookup_l(const char *, unsigned, DNS_RR **, VSTRING *,
- VSTRING *, int,...);
-extern int dns_lookup_v(const char *, unsigned, DNS_RR **, VSTRING *,
- VSTRING *, int, unsigned *);
+extern int dns_lookup_r(const char *, unsigned, unsigned, DNS_RR **,
+ VSTRING *, VSTRING *, int *);
+extern int dns_lookup_rl(const char *, unsigned, DNS_RR **, VSTRING *,
+ VSTRING *, int *, int,...);
+extern int dns_lookup_rv(const char *, unsigned, DNS_RR **, VSTRING *,
+ VSTRING *, int *, int, unsigned *);
+#define dns_lookup(name, type, rflags, list, fqdn, why) \
+ dns_lookup_r((name), (type), (rflags), (list), (fqdn), (why), (int *) 0)
+#define dns_lookup_l(name, rflags, list, fqdn, why, lflags, ...) \
+ dns_lookup_rl((name), (rflags), (list), (fqdn), (why), (int *) 0, \
+ (lflags), __VA_ARGS__)
+#define dns_lookup_v(name, rflags, list, fqdn, why, lflags, ltype) \
+ dns_lookup_rv((name), (rflags), (list), (fqdn), (why), (int *) 0, \
+ (lflags), (ltype))
/*
* Request flags.
/* VSTRING *why;
/* int lflags;
/* unsigned *ltype;
+/* AUXILIARY FUNCTIONS
+/* int dns_lookup_r(name, type, rflags, list, fqdn, why, rcode)
+/* const char *name;
+/* unsigned type;
+/* unsigned rflags;
+/* DNS_RR **list;
+/* VSTRING *fqdn;
+/* VSTRING *why;
+/* int *rcode;
+/*
+/* int dns_lookup_rl(name, rflags, list, fqdn, why, rcode, lflags,
+/* ltype, ...)
+/* const char *name;
+/* unsigned rflags;
+/* DNS_RR **list;
+/* VSTRING *fqdn;
+/* VSTRING *why;
+/* int *rcode;
+/* int lflags;
+/* unsigned ltype;
+/*
+/* int dns_lookup_rv(name, rflags, list, fqdn, why, rcode, lflags,
+/* ltype)
+/* const char *name;
+/* unsigned rflags;
+/* DNS_RR **list;
+/* VSTRING *fqdn;
+/* VSTRING *why;
+/* int *rcode;
+/* int lflags;
+/* unsigned *ltype;
/* DESCRIPTION
/* dns_lookup() looks up DNS resource records. When requested to
/* look up data other than type CNAME, it will follow a limited
/*
/* dns_lookup_l() and dns_lookup_v() allow the user to specify
/* a list of resource types.
+/*
+/* dns_lookup_r(), dns_lookup_rl() and dns_lookup_rv() provide
+/* additional information.
/* INPUTS
/* .ad
/* .fi
/* Request DNSSEC validation. This flag is silently ignored
/* when the system stub resolver API, resolver(3), does not
/* implement DNSSEC.
+/* .IP
+/* Pointer to storage for the reply RCODE value. This gives
+/* more detailed information than DNS_FAIL, DNS_RETRY, etc.
/* .RE
/* .IP lflags
/* Multi-type request control for dns_lookup_l() and dns_lookup_v().
* Structure to keep track of things while decoding a name server reply.
*/
#define DEF_DNS_REPLY_SIZE 4096 /* in case we're using TCP */
-#define MAX_DNS_REPLY_SIZE 32768 /* in case we're using TCP */
+#define MAX_DNS_REPLY_SIZE 65536 /* in case we're using TCP */
typedef struct DNS_REPLY {
unsigned char *buf; /* raw reply data */
size_t buf_len; /* reply buffer length */
+ int rcode; /* unfiltered reply code */
int dnssec_valid; /* DNSSEC AD bit */
int query_count; /* number of queries */
int answer_count; /* number of answers */
len = res_search((char *) name, C_IN, type, reply->buf, reply->buf_len);
_res.options &= ~flags;
_res.options |= saved_options;
+ reply_header = (HEADER *) reply->buf;
+ reply->rcode = reply_header->rcode;
if (len < 0) {
if (why)
vstring_sprintf(why, "Host or domain name not found. "
if (msg_verbose)
msg_info("dns_query: %s (%s): OK", name, dns_strtype(type));
- reply_header = (HEADER *) reply->buf;
if (reply_header->tc == 0 || reply->buf_len >= MAX_DNS_REPLY_SIZE)
break;
reply->buf = (unsigned char *)
return (not_found_status);
}
-/* dns_lookup - DNS lookup user interface */
+/* dns_lookup_r - DNS lookup user interface */
-int dns_lookup(const char *name, unsigned type, unsigned flags,
- DNS_RR **rrlist, VSTRING *fqdn, VSTRING *why)
+int dns_lookup_r(const char *name, unsigned type, unsigned flags,
+ DNS_RR **rrlist, VSTRING *fqdn, VSTRING *why,
+ int *rcode)
{
char cname[DNS_NAME_LEN];
int c_len = sizeof(cname);
/*
* Perform the DNS lookup, and pre-parse the name server reply.
*/
- if ((status = dns_query(name, type, flags, &reply, why)) != DNS_OK)
+ status = dns_query(name, type, flags, &reply, why);
+ if (rcode)
+ *rcode = reply.rcode;
+ if (status != DNS_OK)
return (status);
/*
return (DNS_NOTFOUND);
}
-/* dns_lookup_l - DNS lookup interface with types list */
+/* dns_lookup_rl - DNS lookup interface with types list */
-int dns_lookup_l(const char *name, unsigned flags, DNS_RR **rrlist,
- VSTRING *fqdn, VSTRING *why, int lflags,...)
+int dns_lookup_rl(const char *name, unsigned flags, DNS_RR **rrlist,
+ VSTRING *fqdn, VSTRING *why, int *rcode,
+ int lflags,...)
{
va_list ap;
unsigned type;
if (msg_verbose)
msg_info("lookup %s type %s flags %d",
name, dns_strtype(type), flags);
- status = dns_lookup(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
- fqdn, why);
+ status = dns_lookup_r(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
+ fqdn, why, rcode);
if (status == DNS_OK) {
non_err = 1;
if (rrlist)
return (non_err ? DNS_OK : soft_err ? DNS_RETRY : status);
}
-/* dns_lookup_v - DNS lookup interface with types vector */
+/* dns_lookup_rv - DNS lookup interface with types vector */
-int dns_lookup_v(const char *name, unsigned flags, DNS_RR **rrlist,
- VSTRING *fqdn, VSTRING *why, int lflags,
- unsigned *types)
+int dns_lookup_rv(const char *name, unsigned flags, DNS_RR **rrlist,
+ VSTRING *fqdn, VSTRING *why, int *rcode,
+ int lflags, unsigned *types)
{
unsigned type;
int status = DNS_NOTFOUND;
if (msg_verbose)
msg_info("lookup %s type %s flags %d",
name, dns_strtype(type), flags);
- status = dns_lookup(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
- fqdn, why);
+ status = dns_lookup_r(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
+ fqdn, why, rcode);
if (status == DNS_OK) {
non_err = 1;
if (rrlist)
char *name;
VSTRING *fqdn = vstring_alloc(100);
VSTRING *why = vstring_alloc(100);
+ int rcode;
DNS_RR *rr;
int i;
argv_free(types_argv);
name = argv[2];
msg_verbose = 1;
- switch (dns_lookup_v(name, RES_DEBUG | RES_USE_DNSSEC, &rr, fqdn, why,
- DNS_REQ_FLAG_NONE, types)) {
+ switch (dns_lookup_rv(name, RES_DEBUG | RES_USE_DNSSEC, &rr, fqdn, why,
+ &rcode, DNS_REQ_FLAG_NONE, types)) {
default:
- msg_fatal("%s", vstring_str(why));
+ msg_fatal("%s (rcode=%d)", vstring_str(why), rcode);
case DNS_OK:
printf("%s: fqdn: %s\n", name, vstring_str(fqdn));
print_rr(rr);
(char *) 0, 0, 0);
if (backup) {
dict_mc->backup = dict_open(backup, open_flags, dict_flags);
+ /* Expose backup lock and status to caller. */
+ dict_mc->dict.lock = dict_mc->backup->lock;
+ dict_mc->dict.lock_type = dict_mc->backup->lock_type;
+ dict_mc->dict.lock_fd = dict_mc->backup->lock_fd;
+ dict_mc->dict.stat_fd = dict_mc->backup->stat_fd;
myfree(backup);
} else
dict_mc->backup = 0;
+ /*
+ * Memcached is write-share safe. If the backup database is also
+ * write-share safe, e.g. it has downgraded its persistent lock to
+ * temporary, then expose that downgraded lock to the caller.
+ */
+ if ((dict_flags & DICT_FLAG_OPEN_LOCK) != 0
+ && (dict_mc->backup == 0
+ || dict_mc->backup->lock_fd < 0
+ || ((dict_mc->backup->flags & DICT_FLAG_OPEN_LOCK) == 0
+ && (dict_mc->backup->flags & DICT_FLAG_LOCK) != 0))) {
+ dict_mc->dict.flags &= ~DICT_FLAG_OPEN_LOCK;
+ dict_mc->dict.flags |= DICT_FLAG_LOCK;
+ }
+
/*
* Parse templates and common database parameters. Maps that use
* substring keys should only be used with the full input key.
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20131118"
+#define MAIL_RELEASE_DATE "20131119"
#define MAIL_VERSION_NUMBER "2.11"
#ifdef SNAPSHOT
static void command(VSTREAM *stream, char *fmt,...)
{
- VSTRING *buf;
va_list ap;
- va_list ap2;
va_start(ap, fmt);
* what the program is trying to do.
*/
if (msg_verbose) {
- buf = vstring_alloc(100);
+ va_list ap2;
+
VA_COPY(ap2, ap);
- vstring_vsprintf(buf, fmt, ap2);
+ vmsg_info(fmt, ap2);
va_end(ap2);
- msg_info("%s", vstring_str(buf));
- vstring_free(buf);
}
smtp_vprintf(stream, fmt, ap);
va_end(ap);
int (*sequence) (struct DICT *, int, const char **, const char **);
int (*lock) (struct DICT *, int);
void (*close) (struct DICT *);
- int lock_fd; /* for dict_update() lock */
+ int lock_type; /* for read/write lock */
+ int lock_fd; /* for read/write lock */
int stat_fd; /* change detection */
time_t mtime; /* mod time at open */
VSTRING *fold_buf; /* key folding buffer */
/*
/* One exception is the default lock function. When the
/* dictionary provides a file handle for locking, the default
-/* lock function returns the result from myflock(), otherwise
+/* lock function returns the result from myflock with the
+/* locking method specified in the lock_type member, otherwise
/* it returns 0. Presently, the lock function is used only to
/* implement the DICT_FLAG_OPEN_LOCK feature (lock the database
/* exclusively after it is opened) for databases that are not
static int dict_default_lock(DICT *dict, int operation)
{
if (dict->lock_fd >= 0) {
- return (myflock(dict->lock_fd, INTERNAL_LOCK, operation));
+ return (myflock(dict->lock_fd, dict->lock_type, operation));
} else {
return (0);
}
dict->sequence = dict_default_sequence;
dict->close = dict_default_close;
dict->lock = dict_default_lock;
+ dict->lock_type = INTERNAL_LOCK;
dict->lock_fd = -1;
dict->stat_fd = -1;
dict->mtime = 0;
if (fstat(db_fd, &st) < 0)
msg_fatal("dict_lmdb_open: fstat: %m");
dict_lmdb->dict.lock_fd = dict_lmdb->dict.stat_fd = db_fd;
+ dict_lmdb->dict.lock_type = MYFLOCK_STYLE_FCNTL;
dict_lmdb->dict.mtime = st.st_mtime;
dict_lmdb->dict.owner.uid = st.st_uid;
dict_lmdb->dict.owner.status = (st.st_uid != 0);
if (dict_flags & DICT_FLAG_BULK_UPDATE)
dict_jmp_alloc(&dict_lmdb->dict);
+ /* LMDB is write-share safe; downgrade a persistent lock to temporary. */
+ if (dict_flags & DICT_FLAG_OPEN_LOCK) {
+ dict_lmdb->dict.flags &= ~DICT_FLAG_OPEN_LOCK;
+ dict_lmdb->dict.flags |= DICT_FLAG_LOCK;
+ }
+
/*
* The following requests return an error result only if we have serious
* memory corruption problem.
/* .IP DICT_FLAG_LOCK
/* With maps where this is appropriate, acquire an exclusive lock
/* before writing, and acquire a shared lock before reading.
+/* Release the lock when the operation completes.
/* .IP DICT_FLAG_OPEN_LOCK
/* With databases that are not multi-writer safe, request that
/* dict_open() acquires an exclusive lock, or that it terminates
/* with a fatal run-time error.
+/*
+/* With databases that are multi-writer safe, downgrade from
+/* DICT_FLAG_OPEN_LOCK (persistent lock) to DICT_FLAG_LOCK
+/* (temporary lock).
/* .IP DICT_FLAG_FOLD_FIX
/* With databases whose lookup fields are fixed-case strings,
/* fold the search string to lower case before accessing the
"cannot open %s:%s: %m", dict_type, dict_name));
if (msg_verbose)
msg_info("%s: %s:%s", myname, dict_type, dict_name);
- /* XXX the choice between wait-for-lock or no-wait is hard-coded. */
- if (dict_flags & DICT_FLAG_OPEN_LOCK) {
- if (dict_flags & DICT_FLAG_LOCK)
+ /* Write-share safe maps may downgrade a persistent lock to temporary. */
+ /* XXX The choice between wait-for-lock or no-wait is hard-coded. */
+ if (dict->flags & DICT_FLAG_OPEN_LOCK) {
+ if (dict->flags & DICT_FLAG_LOCK)
msg_panic("%s: attempt to open %s:%s with both \"open\" lock and \"access\" lock",
myname, dict_type, dict_name);
if (dict->lock(dict, MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT) < 0)