Final slution for the I/O event starvation problem when a
timer call-back schedules a zero-delay timer request. File:
util/events.c.
+
+20091231
+
+ Cleanup: the non-shared, in-memory hash table is now
+ accessible as the "internal:" map type. This simplifies
+ code by eliminating some special cases. Files: util/dict_ht.c,
+ util/dict_open.c, and documentation.
+
+20100101
+
+ Bugfix: the mantools/postlink script applied hyperlinks
+ for the "virtual:" transport to "/etc/postfix/virtual:".
+ Symptom reported by Christoph Anton Mitterer.
created with the postmap(1) or postalias(1) command. The database name
as used in "hash:table" is the database file name without the ".db"
suffix.
+ i\bin\bnt\bte\ber\brn\bna\bal\bl
+ A non-shared, in-memory hash table. Its content are lost when a process
+ terminates.
l\bld\bda\bap\bp (read-only)
Perform lookups using the LDAP protocol. Configuration details are
given in the ldap_table(5).
If you upgrade from Postfix 2.5 or earlier, read RELEASE_NOTES-2.6
before proceeding.
-Incompatibility with snapshot 20091229
+Incompatibility with snapshot 20100101
======================================
The verify(8) service now uses a persistent cache by default
When periodic cache cleanup is enabled (the default), the postscreen(8)
and verify(8) servers now require that their cache databases support
the "delete" and "sequence" operations. To disable periodic cache
-cleanup specify a zero xxx_cache_cleanup_interval.
+cleanup specify a zero xxx_cache_cleanup_interval value.
-Major changes with snapshot 20091229
+Major changes with snapshot 20100101
====================================
Periodic cache cleanup for the postscreen(8) and verify(8) cache
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#virtual_alias_maps">virtual_alias_maps</a> = hash:/etc/postfix/virtual
-/etc/postfix/<a href="virtual.8.html">virtual</a>:
+/etc/postfix/virtual:
Wietse.Venema wietse
</pre>
</blockquote>
name as used in "hash:table" is the database file name without the
".db" suffix. </dd>
+<dt> <b>internal</b> </dt>
+
+<dd> A non-shared, in-memory hash table. Its content are lost when
+a process terminates. </dd>
+
<dt> <b>ldap</b> (read-only) </dt>
<dd> Perform lookups using the LDAP protocol. Configuration details
#
root mtaadmin+root=mta1
-/etc/postfix/<a href="virtual.8.html">virtual</a>:
+/etc/postfix/virtual:
# Caretaker aliases:
#
root mtaadmin
9 /etc/postfix/canonical:
10 your-login-name your-account@your-isp.com
11
-12 /etc/postfix/<a href="virtual.8.html">virtual</a>:
+12 /etc/postfix/virtual:
13 your-account@your-isp.com your-login-name
</pre>
</blockquote>
1 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
2 <a href="postconf.5.html#virtual_alias_maps">virtual_alias_maps</a> = hash:/etc/postfix/virtual
3
-4 /etc/postfix/<a href="virtual.8.html">virtual</a>:
+4 /etc/postfix/virtual:
5 postmaster postmaster@example.com
6 abuse abuse@example.com
</pre>
1 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
2 <a href="postconf.5.html#virtual_alias_maps">virtual_alias_maps</a> = hash:/etc/postfix/virtual
3
-4 /etc/postfix/<a href="virtual.8.html">virtual</a>:
+4 /etc/postfix/virtual:
5 root root@localhost
6 . . .
</pre>
9 /etc/postfix/canonical:
10 your-login-name your-account@your-isp.com
11
-12 /etc/postfix/<a href="virtual.8.html">virtual</a>:
+12 /etc/postfix/virtual:
13 your-account@your-isp.com your-login-name
</pre>
</blockquote>
2 <a href="postconf.5.html#virtual_alias_domains">virtual_alias_domains</a> = example.com ...other <a href="VIRTUAL_README.html#canonical">hosted domains</a>...
3 <a href="postconf.5.html#virtual_alias_maps">virtual_alias_maps</a> = hash:/etc/postfix/virtual
4
- 5 /etc/postfix/<a href="virtual.8.html">virtual</a>:
+ 5 /etc/postfix/virtual:
6 postmaster@example.com postmaster
7 info@example.com joe
8 sales@example.com jane
14 # @example.com example.com/catchall
15 ...virtual mailboxes for more domains...
16
-17 /etc/postfix/<a href="virtual.8.html">virtual</a>:
+17 /etc/postfix/virtual:
18 postmaster@example.com postmaster
</pre>
</blockquote>
12 # @example.com whatever
13 ...virtual mailboxes for more domains...
14
-15 /etc/postfix/<a href="virtual.8.html">virtual</a>:
+15 /etc/postfix/virtual:
16 postmaster@example.com postmaster
</pre>
</blockquote>
2 <a href="postconf.5.html#virtual_alias_domains">virtual_alias_domains</a> = example.com ...other <a href="VIRTUAL_README.html#canonical">hosted domains</a>...
3 <a href="postconf.5.html#virtual_alias_maps">virtual_alias_maps</a> = hash:/etc/postfix/virtual
4
- 5 /etc/postfix/<a href="virtual.8.html">virtual</a>:
+ 5 /etc/postfix/virtual:
6 postmaster@example.com postmaster
7 joe@example.com joe@somewhere
8 jane@example.com jane@somewhere-else
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#virtual_alias_maps">virtual_alias_maps</a> = hash:/etc/postfix/virtual
-/etc/postfix/<a href="virtual.8.html">virtual</a>:
+/etc/postfix/virtual:
listname-request@example.com listname-request
listname@example.com listname
owner-listname@example.com owner-listname
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#virtual_alias_maps">virtual_alias_maps</a> = hash:/etc/postfix/virtual
-/etc/postfix/<a href="virtual.8.html">virtual</a>:
+/etc/postfix/virtual:
user@domain.tld user@domain.tld, user@domain.tld@autoreply.<a href="postconf.5.html#mydomain">mydomain</a>.tld
</pre>
</blockquote>
is available on systems with support for
Berkeley DB databases.
+ <b>internal</b>
+ A non-shared, in-memory hash table. Its con-
+ tent are lost when a process terminates.
+
<b>ldap</b> (read-only)
- Perform lookups using the LDAP protocol.
+ Perform lookups using the LDAP protocol.
This is described in <a href="ldap_table.5.html"><b>ldap_table</b>(5)</a>.
<b>mysql</b> (read-only)
- Perform lookups using the MYSQL protocol.
+ Perform lookups using the MYSQL protocol.
This is described in <a href="mysql_table.5.html"><b>mysql_table</b>(5)</a>.
<b>pcre</b> (read-only)
A lookup table based on Perl Compatible Reg-
- ular Expressions. The file format is
+ ular Expressions. The file format is
described in <a href="pcre_table.5.html"><b>pcre_table</b>(5)</a>.
<b>pgsql</b> (read-only)
- Perform lookups using the PostgreSQL proto-
+ Perform lookups using the PostgreSQL proto-
col. This is described in <a href="pgsql_table.5.html"><b>pgsql_table</b>(5)</a>.
<b>proxy</b> (read-only)
- A lookup table that is implemented via the
- Postfix <a href="proxymap.8.html"><b>proxymap</b>(8)</a> service. The table name
+ A lookup table that is implemented via the
+ Postfix <a href="proxymap.8.html"><b>proxymap</b>(8)</a> service. The table name
syntax is <i>type</i><b>:</b><i>name</i>.
<b>regexp</b> (read-only)
A lookup table based on regular expressions.
- The file format is described in <a href="regexp_table.5.html"><b>regexp_ta-</b></a>
+ The file format is described in <a href="regexp_table.5.html"><b>regexp_ta-</b></a>
<a href="regexp_table.5.html"><b>ble</b>(5)</a>.
<b>sdbm</b> An indexed file type based on hashing. This
- is available on systems with support for
+ is available on systems with support for
SDBM databases.
<b>static</b> (read-only)
- A table that always returns its name as
- lookup result. For example, <b>static:foobar</b>
- always returns the string <b>foobar</b> as lookup
+ A table that always returns its name as
+ lookup result. For example, <b>static:foobar</b>
+ always returns the string <b>foobar</b> as lookup
result.
<b>tcp</b> (read-only)
Perform lookups using a simple request-reply
- protocol that is described in <a href="tcp_table.5.html"><b>tcp_table</b>(5)</a>.
+ protocol that is described in <a href="tcp_table.5.html"><b>tcp_table</b>(5)</a>.
This feature is not included with the stable
Postfix release.
<b>unix</b> (read-only)
- A limited way to query the UNIX authentica-
+ A limited way to query the UNIX authentica-
tion database. The following tables are
implemented:
<b>unix:passwd.byname</b>
- The table is the UNIX password data-
- base. The key is a login name. The
- result is a password file entry in
+ The table is the UNIX password data-
+ base. The key is a login name. The
+ result is a password file entry in
<b>passwd</b>(5) format.
<b>unix:group.byname</b>
The table is the UNIX group database.
- The key is a group name. The result
- is a group file entry in <b>group</b>(5)
+ The key is a group name. The result
+ is a group file entry in <b>group</b>(5)
format.
- Other table types may exist depending on how Post-
+ Other table types may exist depending on how Post-
fix was built.
<b>-n</b> Print parameter settings that are not left at their
<b>-t</b> [<i>template</i><b>_</b><i>file</i>]
Display the templates for delivery status notifica-
- tion (DSN) messages. To override the built-in tem-
- plates, specify a template file at the end of the
+ tion (DSN) messages. To override the built-in tem-
+ plates, specify a template file at the end of the
command line, or specify a template file in <a href="postconf.5.html">main.cf</a>
- with the <b><a href="postconf.5.html#bounce_template_file">bounce_template_file</a></b> parameter. To force
- selection of the built-in templates, specify an
+ with the <b><a href="postconf.5.html#bounce_template_file">bounce_template_file</a></b> parameter. To force
+ selection of the built-in templates, specify an
empty template file name (in shell language: "").
- This feature is available with Postfix 2.3 and
+ This feature is available with Postfix 2.3 and
later.
<b>-v</b> Enable verbose logging for debugging purposes. Mul-
- tiple <b>-v</b> options make the software increasingly
+ tiple <b>-v</b> options make the software increasingly
verbose.
- <b>-#</b> Edit the <a href="postconf.5.html"><b>main.cf</b></a> configuration file. The file is
+ <b>-#</b> Edit the <a href="postconf.5.html"><b>main.cf</b></a> configuration file. The file is
copied to a temporary file then renamed into place.
- The parameters specified on the command line are
+ The parameters specified on the command line are
commented-out, so that they revert to their default
- values. Specify a list of parameter names, not
- name=value pairs. There is no <b>postconf</b> command to
+ values. Specify a list of parameter names, not
+ name=value pairs. There is no <b>postconf</b> command to
perform the reverse operation.
- This feature is available with Postfix 2.6 and
+ This feature is available with Postfix 2.6 and
later.
<b>DIAGNOSTICS</b>
Directory with Postfix configuration files.
<b>CONFIGURATION PARAMETERS</b>
- The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant
+ The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant
to this program.
- The text below provides only a parameter summary. See
+ The text below provides only a parameter summary. See
<a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including examples.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
<a href="master.5.html">master.cf</a> configuration files.
<b><a href="postconf.5.html#bounce_template_file">bounce_template_file</a> (empty)</b>
- Pathname of a configuration file with bounce mes-
+ Pathname of a configuration file with bounce mes-
sage templates.
<b>FILES</b>
<a href="DATABASE_README.html">DATABASE_README</a>, Postfix lookup table overview
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
from filling up with undeliverable MAILER-DAEMON messages.
</p>
+<p> An address is always considered "known" when it matches a
+<a href="virtual.5.html">virtual(5)</a> alias or a <a href="canonical.5.html">canonical(5)</a> mapping.
+
<ul>
<li> The recipient domain matches $<a href="postconf.5.html#mydestination">mydestination</a>, $<a href="postconf.5.html#inet_interfaces">inet_interfaces</a>
access restriction is specified. This can slow down an explosion
of forged mail from worms or viruses. </p>
+<p> An address is always considered "known" when it matches a
+<a href="virtual.5.html">virtual(5)</a> alias or a <a href="canonical.5.html">canonical(5)</a> mapping.
+
<ul>
<li> The sender domain matches $<a href="postconf.5.html#mydestination">mydestination</a>, $<a href="postconf.5.html#inet_interfaces">inet_interfaces</a> or
(default: 51200000)</b></DT><DD>
<p>
-The maximal size in bytes of an individual mailbox or maildir file,
-or zero (no limit).
-</p>
+The maximal size in bytes of an individual <a href="virtual.8.html">virtual(8)</a> mailbox or
+maildir file, or zero (no limit). </p>
</DD>
the output from "<b>postconf -m</b>" for available database
types.
- /etc/postfix/<a href="virtual.8.html">virtual</a>:
+ /etc/postfix/virtual:
<i>virtual-alias.domain anything</i> (right-hand content does not matter)
<i>postmaster@virtual-alias.domain postmaster</i>
<i>user1@virtual-alias.domain address1</i>
An indexed file type based on hashing.
This is available on systems with support for Berkeley DB
databases.
+.IP \fBinternal\fR
+A non-shared, in-memory hash table. Its content are lost
+when a process terminates.
.IP "\fBldap\fR (read-only)"
Perform lookups using the LDAP protocol. This is described
in \fBldap_table\fR(5).
recipient addresses, even when no explicit reject_unlisted_recipient
access restriction is specified. This prevents the Postfix queue
from filling up with undeliverable MAILER-DAEMON messages.
+.PP
+An address is always considered "known" when it matches a
+\fBvirtual\fR(5) alias or a \fBcanonical\fR(5) mapping.
.IP \(bu
The recipient domain matches $mydestination, $inet_interfaces
or $proxy_interfaces, but the recipient is not listed in
sender addresses, even when no explicit reject_unlisted_sender
access restriction is specified. This can slow down an explosion
of forged mail from worms or viruses.
+.PP
+An address is always considered "known" when it matches a
+\fBvirtual\fR(5) alias or a \fBcanonical\fR(5) mapping.
.IP \(bu
The sender domain matches $mydestination, $inet_interfaces or
$proxy_interfaces, but the sender is not listed in
This feature is available in Postfix 2.0 and later. The default
value is backwards compatible with Postfix version 1.1.
.SH virtual_mailbox_limit (default: 51200000)
-The maximal size in bytes of an individual mailbox or maildir file,
-or zero (no limit).
+The maximal size in bytes of an individual \fBvirtual\fR(8) mailbox or
+maildir file, or zero (no limit).
.SH virtual_mailbox_lock (default: see "postconf -d" output)
How to lock a UNIX-style \fBvirtual\fR(8) mailbox before attempting
delivery. For a list of available file locking methods, use the
s/\b(smtp):/<a href="smtp.8.html">$1<\/a>:/g;
s/\b(lmtp):/<a href="lmtp.8.html">$1<\/a>:/g;
s/\b(local):/<a href="local.8.html">$1<\/a>:/g;
- s/\b(virtual):/<a href="virtual.8.html">$1<\/a>:/g;
+ s/([^\/])\b(virtual):/$1<a href="virtual.8.html">$2<\/a>:/g;
}
continue {
if ($printit)
name as used in "hash:table" is the database file name without the
".db" suffix. </dd>
+<dt> <b>internal</b> </dt>
+
+<dd> A non-shared, in-memory hash table. Its content are lost when
+a process terminates. </dd>
+
<dt> <b>ldap</b> (read-only) </dt>
<dd> Perform lookups using the LDAP protocol. Configuration details
from filling up with undeliverable MAILER-DAEMON messages.
</p>
+<p> An address is always considered "known" when it matches a
+virtual(5) alias or a canonical(5) mapping.
+
<ul>
<li> The recipient domain matches $mydestination, $inet_interfaces
access restriction is specified. This can slow down an explosion
of forged mail from worms or viruses. </p>
+<p> An address is always considered "known" when it matches a
+virtual(5) alias or a canonical(5) mapping.
+
<ul>
<li> The sender domain matches $mydestination, $inet_interfaces or
%PARAM virtual_mailbox_limit 51200000
<p>
-The maximal size in bytes of an individual mailbox or maildir file,
-or zero (no limit).
-</p>
+The maximal size in bytes of an individual virtual(8) mailbox or
+maildir file, or zero (no limit). </p>
%PARAM virtual_mailbox_lock see "postconf -d" output
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20091229"
+#define MAIL_RELEASE_DATE "20100101"
#define MAIL_VERSION_NUMBER "2.7"
#ifdef SNAPSHOT
/* An indexed file type based on hashing.
/* This is available on systems with support for Berkeley DB
/* databases.
+/* .IP \fBinternal\fR
+/* A non-shared, in-memory hash table. Its content are lost
+/* when a process terminates.
/* .IP "\fBldap\fR (read-only)"
/* Perform lookups using the LDAP protocol. This is described
/* in \fBldap_table\fR(5).
* verbose logging more informative (we get positive confirmation that
* the cleanup thread runs).
*/
- expire_flags = DICT_CACHE_FLAG_EXP_SUMMARY;
+ expire_flags = DICT_CACHE_FLAG_STATISTICS;
if (msg_verbose)
- expire_flags |= DICT_CACHE_FLAG_EXP_VERBOSE;
+ expire_flags |= DICT_CACHE_FLAG_VERBOSE;
if (cache_map != 0 && var_ps_cache_scan > 0)
- dict_cache_expire(cache_map, expire_flags, var_ps_cache_scan,
- postscreen_cache_validator, (char *) 0);
+ dict_cache_control(cache_map,
+ DICT_CACHE_CTL_FLAGS, expire_flags,
+ DICT_CACHE_CTL_INTERVAL, var_ps_cache_scan,
+ DICT_CACHE_CTL_VALIDATOR, postscreen_cache_validator,
+ DICT_CACHE_CTL_CONTEXT, (char *) 0,
+ DICT_CACHE_CTL_END);
}
MAIL_VERSION_STAMP_DECLARE;
>>> helo verisign.com
OK
>>> helo example.tld
-./smtpd_check: warning: Unable to look up MX host for example.tld: Host not found
+./smtpd_check: warning: Unable to look up MX host example.tld for Helo command example.tld: hostname nor servname provided, or not known
OK
>>> sender_restrictions check_sender_mx_access,hash:smtpd_check_access
OK
dict_ht.o: dict_ht.h
dict_ht.o: htable.h
dict_ht.o: mymalloc.h
+dict_ht.o: stringops.h
dict_ht.o: sys_defs.h
dict_ht.o: vbuf.h
dict_ht.o: vstream.h
dict_open.o: dict_db.h
dict_open.o: dict_dbm.h
dict_open.o: dict_env.h
+dict_open.o: dict_ht.h
dict_open.o: dict_ni.h
dict_open.o: dict_nis.h
dict_open.o: dict_nisplus.h
if ((node = dict_node(dict_name)) == 0) {
if (dict_unknown_allowed == 0)
msg_fatal("%s: unknown dictionary: %s", myname, dict_name);
- dict = dict_ht_open(dict_name, htable_create(0), myfree);
+ dict = dict_ht_open(dict_name, O_CREAT | O_RDWR, 0);
dict_register(dict_name, dict);
} else
dict = node->dict;
if ((node = dict_node(dict_name)) == 0) {
if (dict_unknown_allowed == 0)
msg_fatal("%s: unknown dictionary: %s", myname, dict_name);
- dict = dict_ht_open(dict_name, htable_create(0), myfree);
+ dict = dict_ht_open(dict_name, O_CREAT | O_RDWR, 0);
dict_register(dict_name, dict);
} else
dict = node->dict;
if ((node = dict_node(dict_name)) == 0) {
if (dict_unknown_allowed == 0)
msg_fatal("%s: unknown dictionary: %s", myname, dict_name);
- dict = dict_ht_open(dict_name, htable_create(0), myfree);
+ dict = dict_ht_open(dict_name, O_CREAT | O_RDWR, 0);
dict_register(dict_name, dict);
} else
dict = node->dict;
/* int first_next;
/* const char **cache_key;
/* const char **cache_val;
-/*
-/* void dict_cache_expire(cache, flags, interval, validator, context)
-/* DICT_CACHE *cache;
-/* int flags;
-/* int interval;
-/* int (*validator)(const char *cache_key, const char *cache_val,
-/* char *context);
-/* char *context;
/* AUXILIARY FUNCTIONS
+/* void dict_cache_control(cache, name, value, ...)
+/* DICT_CACHE *cache;
+/* int name;
+/*
+/* typedef int (*DICT_CACHE_VALIDATOR_FN) (const char *cache_key,
+/* const char *cache_val, char *context);
+/*
/* const char *dict_cache_name(cache)
/* DICT_CACHE *cache;
-/*
-/* DICT_CACHE *dict_cache_import(table)
-/* DICT *table;
/* DESCRIPTION
/* This module maintains external cache files with support
/* for expiration. The underlying table must implement the
/* between the iterators that access all cache elements, and
/* other operations that access individual cache elements.
/*
-/* In particular, when a "sequence" or "expire" operation is
+/* In particular, when a "sequence" or "cleanup" operation is
/* in progress the cache intercepts requests to delete the
/* "current" entry, as this would cause some databases to
/* mis-behave. Instead, the cache implements a "delete behind"
/* strategy, and deletes such an entry after the "sequence"
-/* or "expire" operation moves on to the next cache element.
+/* or "cleanup" operation moves on to the next cache element.
/* The "delete behind" strategy also affects the cache lookup
/* and update operations as detailed below.
/*
-/* dict_cache_open() opens the specified cache and returns a
-/* handle that must be used for subsequent access. This function
-/* does not return in case of error.
+/* dict_cache_open() is a wrapper around the dict_open()
+/* function. It opens the specified cache and returns a handle
+/* that must be used for subsequent access. This function does
+/* not return in case of error.
/*
/* dict_cache_close() closes the specified cache and releases
/* memory that was allocated by dict_cache_open(), and terminates
-/* any thread that was started with dict_cache_expire().
+/* any thread that was started with dict_cache_control().
/*
/* dict_cache_lookup() looks up the specified cache entry.
/* The result value is a null pointer when the cache entry was
/*
/* dict_cache_update() updates the specified cache entry. If
/* the entry is scheduled for "delete behind", the delete
-/* operation is canceled (meaning that the cache must be opened
-/* with DICT_FLAG_DUP_REPLACE). This function does not return
-/* in case of error.
+/* operation is canceled (because of this, the cache must be
+/* opened with DICT_FLAG_DUP_REPLACE). This function does not
+/* return in case of error.
/*
/* dict_cache_delete() removes the specified cache entry. If
/* this is the "current" entry of a "sequence" operation, the
/*
/* dict_cache_sequence() iterates over the specified cache and
/* returns each entry in an implementation-defined order. The
-/* result value is zero when a cache entry was found. Programs
-/* must not use both dict_cache_sequence() and dict_cache_expire().
+/* result value is zero when a cache entry was found.
/*
-/* dict_cache_expire() schedules a thread that expires cache
-/* entries periodically. Specify a null validator argument to
-/* cancel the thread. It is an error to schedule a cache
-/* cleanup thread when one already exists. Programs must not
-/* use both dict_cache_sequence() and dict_cache_expire().
+/* Important: programs must not use both dict_cache_sequence()
+/* and the built-in cache cleanup feature.
/*
+/* dict_cache_control() provides control over the built-in
+/* cache cleanup feature and logging. The arguments are a list
+/* of (name, value) pairs, terminated with DICT_CACHE_CTL_END.
+/* The following lists the names and the types of the corresponding
+/* value arguments.
+/* .IP "DICT_CACHE_FLAGS (int flags)"
+/* The arguments to this command are the bit-wise OR of zero
+/* or more of the following:
+/* .RS
+/* .IP DICT_CACHE_FLAG_VERBOSE
+/* Enable verbose logging of cache activity.
+/* .IP DICT_CACHE_FLAG_EXP_SUMMARY
+/* Log cache statistics after each cache cleanup run.
+/* .RE
+/* .IP "DICT_CACHE_CTL_INTERVAL (int interval)"
+/* The interval between cache cleanup runs. Specify a null
+/* validator or interval to stop cache cleanup.
+/* .IP "DICT_CACHE_CTL_VALIDATOR (DICT_CACHE_VALIDATOR_FN validator)"
+/* An application call-back routine that returns non-zero when
+/* a cache entry should be kept. The call-back function should
+/* not make changes to the cache. Specify a null validator or
+/* interval to stop cache cleanup.
+/* .IP "DICT_CACHE_CTL_CONTEXT (char *context)"
+/* Application context that is passed to the validator function.
+/* .RE
+/* .PP
/* dict_cache_name() returns the name of the specified cache.
/*
-/* dict_cache_import() encapsulates a pre-opened database
-/* handle and adds the above features.
-/*
/* Arguments:
/* .IP "dbname, open_flags, dict_flags"
-/* These are passed unchanged to dict_open().
+/* These are passed unchanged to dict_open(). The cache must
+/* be opened with DICT_FLAG_DUP_REPLACE.
/* .IP cache
-/* Cache handle created with dict_cache_open()or dict_cache_import().
+/* Cache handle created with dict_cache_open().
/* .IP cache_key
/* Cache lookup key.
/* .IP cache_val
/* Note: there is no "stop" request. To ensure that the "delete
/* behind" strategy does not interfere with database access,
/* allow dict_cache_sequence() to run to completion.
-/* .IP flags
-/* Bit-wise OR of zero or more of the following:
-/* .RS
-/* .IP DICT_CACHE_FLAG_EXP_VERBOSE
-/* Log each cache entry's status during a cache cleanup run.
-/* .IP DICT_CACHE_FLAG_EXP_SUMMARY
-/* Log the number of cache entries retained and dropped after
-/* a cache cleaning run.
-/* .RE
-/* .IP interval
-/* The non-zero time between scans for expired cache entries.
-/* The interval timer starts after a scan completes.
-/* .IP validator
-/* Application call-back routine that returns non-zero when a
-/* cache entry should be kept. The validator must not modify
-/* or close the cache.
-/* .IP context
-/* Application-specific context.
/* .IP table
/* A bare dictonary handle.
/* DIAGNOSTICS
* underlying database.
*/
struct DICT_CACHE {
- int flags; /* see below */
+ int cache_flags; /* see below */
+ int user_flags; /* logging */
DICT *db; /* database handle */
- /* Iterator support. */
+ /* Delete-behind support. */
char *saved_curr_key; /* "current" cache lookup key */
char *saved_curr_val; /* "current" cache lookup result */
/* Cleanup support. */
- int exp_flags; /* logging */
int exp_interval; /* time between cleanup runs */
DICT_CACHE_VALIDATOR_FN exp_validator; /* expiration call-back */
char *exp_context; /* call-back context */
* Macros to make obscure code more readable.
*/
#define DC_SCHEDULE_FOR_DELETE_BEHIND(cp) \
- ((cp)->flags |= DC_FLAG_DEL_SAVED_CURRENT_KEY)
+ ((cp)->cache_flags |= DC_FLAG_DEL_SAVED_CURRENT_KEY)
#define DC_MATCH_SAVED_CURRENT_KEY(cp, cache_key) \
((cp)->saved_curr_key && strcmp((cp)->saved_curr_key, (cache_key)) == 0)
#define DC_IS_SCHEDULED_FOR_DELETE_BEHIND(cp) \
(/* NOT: (cp)->saved_curr_key && */ \
- ((cp)->flags & DC_FLAG_DEL_SAVED_CURRENT_KEY) != 0)
+ ((cp)->cache_flags & DC_FLAG_DEL_SAVED_CURRENT_KEY) != 0)
#define DC_CANCEL_DELETE_BEHIND(cp) \
- ((cp)->flags &= ~DC_FLAG_DEL_SAVED_CURRENT_KEY)
+ ((cp)->cache_flags &= ~DC_FLAG_DEL_SAVED_CURRENT_KEY)
/*
- * Special key to store the time of last cache cleanup run completion.
+ * Special key to store the time of the last cache cleanup run completion.
*/
#define DC_LAST_CACHE_CLEANUP_COMPLETED "_LAST_CACHE_CLEANUP_COMPLETED_"
const char *dict_cache_lookup(DICT_CACHE *cp, const char *cache_key)
{
+ const char *myname = "dict_cache_lookup";
+ const char *cache_val;
/*
- * Search for the cache entry. Don't return an entry that was scheduled
- * for deletion.
+ * Search for the cache entry. Don't return an entry that is scheduled
+ * for delete-behind.
*/
if (DC_IS_SCHEDULED_FOR_DELETE_BEHIND(cp)
&& DC_MATCH_SAVED_CURRENT_KEY(cp, cache_key)) {
+ if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
+ msg_info("%s: key=%s (pretend not found - scheduled for deletion)",
+ myname, cache_key);
return (0);
} else {
- return (dict_get(cp->db, cache_key));
+ cache_val = dict_get(cp->db, cache_key);
+ if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
+ msg_info("%s: key=%s value=%s", myname, cache_key,
+ cache_val ? cache_val : "(not found)");
+ return (cache_val);
}
}
void dict_cache_update(DICT_CACHE *cp, const char *cache_key,
const char *cache_val)
{
+ const char *myname = "dict_cache_update";
/*
- * Store the cache entry and cancel a scheduled delete-behind operation.
+ * Store the cache entry and cancel the delete-behind operation.
*/
if (DC_IS_SCHEDULED_FOR_DELETE_BEHIND(cp)
- && DC_MATCH_SAVED_CURRENT_KEY(cp, cache_key))
+ && DC_MATCH_SAVED_CURRENT_KEY(cp, cache_key)) {
+ if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
+ msg_info("%s: cancel delete-behind for key=%s", myname, cache_key);
DC_CANCEL_DELETE_BEHIND(cp);
+ }
+ if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
+ msg_info("%s: key=%s value=%s", myname, cache_key, cache_val);
dict_put(cp->db, cache_key, cache_val);
}
int dict_cache_delete(DICT_CACHE *cp, const char *cache_key)
{
+ const char *myname = "dict_cache_delete";
int zero_means_found;
/*
* Delete the entry, unless we would delete the current first/next entry.
- * Instead, schedule the "current" entry for delete-behind to avoid
+ * In that case, schedule the "current" entry for delete-behind to avoid
* mis-behavior by some databases.
*/
if (DC_MATCH_SAVED_CURRENT_KEY(cp, cache_key)) {
DC_SCHEDULE_FOR_DELETE_BEHIND(cp);
+ if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
+ msg_info("%s: key=%s (current entry - schedule for delete-behind)",
+ myname, cache_key);
zero_means_found = 0;
} else {
zero_means_found = dict_del(cp->db, cache_key);
+ if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
+ msg_info("%s: key=%s (%s)", myname, cache_key,
+ zero_means_found == 0 ? "found" : "not found");
}
return (zero_means_found);
}
const char **cache_key,
const char **cache_val)
{
+ const char *myname = "dict_cache_sequence";
int zero_means_found;
const char *raw_cache_key;
const char *raw_cache_val;
&& strcmp(raw_cache_key, DC_LAST_CACHE_CLEANUP_COMPLETED) == 0)
zero_means_found =
dict_seq(cp->db, DICT_SEQ_FUN_NEXT, &raw_cache_key, &raw_cache_val);
+ if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
+ msg_info("%s: key=%s value=%s", myname,
+ zero_means_found == 0 ? raw_cache_key : "(not found)",
+ zero_means_found == 0 ? raw_cache_val : "(not found)");
/*
* Save the current cache_key and cache_val before they are clobbered by
*/
if (DC_IS_SCHEDULED_FOR_DELETE_BEHIND(cp)) {
DC_CANCEL_DELETE_BEHIND(cp);
+ if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
+ msg_info("%s: delete-behind key=%s value=%s",
+ myname, previous_curr_key, previous_curr_val);
if (dict_del(cp->db, previous_curr_key) != 0)
msg_warn("database %s: could not delete entry for %s",
cp->db->name, previous_curr_key);
/* dict_cache_clean_stat_log_reset - log and reset cache cleanup statistics */
static void dict_cache_clean_stat_log_reset(DICT_CACHE *cp,
- const char *full_partial)
+ const char *full_partial)
{
- if (cp->flags & DICT_CACHE_FLAG_EXP_SUMMARY)
+ if (cp->user_flags & DICT_CACHE_FLAG_STATISTICS)
msg_info("cache %s %s cleanup: retained=%d dropped=%d entries",
cp->db->name, full_partial, cp->retained, cp->dropped);
cp->retained = cp->dropped = 0;
}
-/* dict_cache_expire_event - examine one cache entry */
+/* dict_cache_clean_event - examine one cache entry */
-static void dict_cache_expire_event(int unused_event, char *cache_context)
+static void dict_cache_clean_event(int unused_event, char *cache_context)
{
+ const char *myname = "dict_cache_clean_event";
DICT_CACHE *cp = (DICT_CACHE *) cache_context;
const char *cache_key;
const char *cache_val;
if (cp->saved_curr_key == 0) {
cp->retained = cp->dropped = 0;
first_next = DICT_SEQ_FUN_FIRST;
- if (cp->exp_flags & DICT_CACHE_FLAG_EXP_VERBOSE)
- msg_info("start %s cache cleanup", cp->db->name);
+ if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
+ msg_info("%s: start %s cache cleanup", myname, cp->db->name);
}
/*
if (cp->exp_validator(cache_key, cache_val, cp->exp_context) == 0) {
DC_SCHEDULE_FOR_DELETE_BEHIND(cp);
cp->dropped++;
- if (cp->exp_flags & DICT_CACHE_FLAG_EXP_VERBOSE)
- msg_info("drop %s cache entry for %s", cp->db->name, cache_key);
+ if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
+ msg_info("%s: drop %s cache entry for %s",
+ myname, cp->db->name, cache_key);
} else {
cp->retained++;
- if (cp->exp_flags & DICT_CACHE_FLAG_EXP_VERBOSE)
- msg_info("keep %s cache entry for %s", cp->db->name, cache_key);
+ if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
+ msg_info("%s: keep %s cache entry for %s",
+ myname, cp->db->name, cache_key);
}
next_interval = 0;
}
* Cache cleanup completed. Report vital statistics.
*/
else {
- if (cp->exp_flags & DICT_CACHE_FLAG_EXP_VERBOSE)
- msg_info("done %s cache cleanup scan", cp->db->name);
+ if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
+ msg_info("%s: done %s cache cleanup scan", myname, cp->db->name);
dict_cache_clean_stat_log_reset(cp, "full");
stamp_buf = vstring_alloc(100);
vstring_sprintf(stamp_buf, "%ld", (long) event_time());
vstring_free(stamp_buf);
next_interval = cp->exp_interval;
}
- event_request_timer(dict_cache_expire_event, cache_context, next_interval);
+ event_request_timer(dict_cache_clean_event, cache_context, next_interval);
}
-/* dict_cache_expire - schedule or stop the cache cleanup thread */
+/* dict_cache_control - schedule or stop the cache cleanup thread */
-void dict_cache_expire(DICT_CACHE *cp, int flags, int interval,
- DICT_CACHE_VALIDATOR_FN validator,
- char *context)
+void dict_cache_control(DICT_CACHE *cp,...)
{
- const char *myname = "dict_cache_expire";
+ const char *myname = "dict_cache_control";
const char *last_done;
time_t next_interval;
+ int cache_cleanup_is_active = (cp->exp_validator && cp->exp_interval);
+ va_list ap;
+ int name;
+
+ /*
+ * Update the control settings.
+ */
+ va_start(ap, cp);
+ while ((name = va_arg(ap, int)) > 0) {
+ switch (name) {
+ case DICT_CACHE_CTL_END:
+ break;
+ case DICT_CACHE_CTL_FLAGS:
+ cp->user_flags = va_arg(ap, int);
+ break;
+ case DICT_CACHE_CTL_INTERVAL:
+ cp->exp_interval = va_arg(ap, int);
+ if (cp->exp_interval < 0)
+ msg_panic("%s: bad %s cache cleanup interval %d",
+ myname, cp->db->name, cp->exp_interval);
+ break;
+ case DICT_CACHE_CTL_VALIDATOR:
+ cp->exp_validator = va_arg(ap, DICT_CACHE_VALIDATOR_FN);
+ break;
+ case DICT_CACHE_CTL_CONTEXT:
+ cp->exp_context = va_arg(ap, char *);
+ break;
+ default:
+ msg_panic("%s: bad command: %d", myname, name);
+ }
+ }
+ va_end(ap);
/*
* Schedule the cache cleanup thread.
*/
- if (validator != 0) {
+ if (cp->exp_interval && cp->exp_validator) {
/*
* Sanity checks.
*/
- if (cp->exp_validator != 0)
+ if (cache_cleanup_is_active)
msg_panic("%s: %s cache cleanup is already scheduled",
myname, cp->db->name);
- if (interval <= 0)
- msg_panic("%s: bad %s cache cleanup interval %d",
- myname, cp->db->name, interval);
- cp->exp_flags = flags;
- cp->exp_interval = interval;
- cp->exp_validator = validator;
- cp->exp_context = context;
/*
* The next start time depends on the last completion time.
#define NOW (time((time_t *) 0)) /* NOT: event_time() */
if ((last_done = dict_get(cp->db, DC_LAST_CACHE_CLEANUP_COMPLETED)) == 0
- || (next_interval = (NEXT_START(last_done, interval) - NOW)) < 0)
+ || (next_interval = (NEXT_START(last_done, cp->exp_interval) - NOW)) < 0)
next_interval = 0;
- if (next_interval > interval)
- next_interval = interval;
- if ((cp->exp_flags & DICT_CACHE_FLAG_EXP_VERBOSE) && next_interval > 0)
+ if (next_interval > cp->exp_interval)
+ next_interval = cp->exp_interval;
+ if ((cp->user_flags & DICT_CACHE_FLAG_VERBOSE) && next_interval > 0)
msg_info("%s cache cleanup will start after %ds",
cp->db->name, (int) next_interval);
- event_request_timer(dict_cache_expire_event, (char *) cp,
+ event_request_timer(dict_cache_clean_event, (char *) cp,
(int) next_interval);
}
/*
* Cancel the cache cleanup thread.
*/
- else if (cp->exp_validator) {
+ else if (cache_cleanup_is_active) {
if (cp->retained || cp->dropped)
dict_cache_clean_stat_log_reset(cp, "partial");
dict_cache_delete_behind_reset(cp);
- cp->exp_interval = 0;
- cp->exp_validator = 0;
- cp->exp_context = 0;
- event_cancel_timer(dict_cache_expire_event, (char *) cp);
+ event_cancel_timer(dict_cache_clean_event, (char *) cp);
}
}
DICT_CACHE *dict_cache_open(const char *dbname, int open_flags, int dict_flags)
{
+ DICT_CACHE *cp;
DICT *dict;
/*
* application.
*/
dict = dict_open(dbname, open_flags, dict_flags);
- return (dict_cache_import(dict));
-}
-
-/* dict_cache_import - encapsulate pre-opened database */
-
-DICT_CACHE *dict_cache_import(DICT *dict)
-{
- DICT_CACHE *cp;
/*
* Create the DICT_CACHE object.
*/
cp = (DICT_CACHE *) mymalloc(sizeof(*cp));
- cp->flags = 0;
+ cp->cache_flags = 0;
+ cp->user_flags = 0;
cp->db = dict;
cp->saved_curr_key = 0;
cp->saved_curr_val = 0;
/*
* Destroy the DICT_CACHE object.
*/
- if (cp->exp_validator)
- dict_cache_expire(cp, 0, 0, (DICT_CACHE_VALIDATOR_FN) 0, (char *) 0);
+ dict_cache_control(cp, DICT_CACHE_CTL_INTERVAL, 0, DICT_CACHE_CTL_END);
dict_close(cp->db);
if (cp->saved_curr_key)
myfree(cp->saved_curr_key);
extern void dict_cache_update(DICT_CACHE *, const char *, const char *);
extern int dict_cache_delete(DICT_CACHE *, const char *);
extern int dict_cache_sequence(DICT_CACHE *, int, const char **, const char **);
-extern void dict_cache_expire(DICT_CACHE *, int, int, DICT_CACHE_VALIDATOR_FN, char *);
+extern void dict_cache_control(DICT_CACHE *,...);
extern const char *dict_cache_name(DICT_CACHE *);
-extern DICT_CACHE *dict_cache_import(DICT *);
-#define DICT_CACHE_FLAG_EXP_VERBOSE (1<<0)
-#define DICT_CACHE_FLAG_EXP_SUMMARY (1<<1)
+#define DICT_CACHE_FLAG_VERBOSE (1<<0) /* verbose operation */
+#define DICT_CACHE_FLAG_STATISTICS (1<<1) /* log cache statistics */
+
+#define DICT_CACHE_CTL_END 0 /* list terminator */
+#define DICT_CACHE_CTL_FLAGS 1 /* see above */
+#define DICT_CACHE_CTL_INTERVAL 2 /* cleanup interval */
+#define DICT_CACHE_CTL_VALIDATOR 3 /* call-back validator */
+#define DICT_CACHE_CTL_CONTEXT 4 /* call-back context */
/* LICENSE
/* .ad
/* SYNOPSIS
/* #include <dict_ht.h>
/*
-/* DICT *dict_ht_open(name, table, remove)
+/* DICT *dict_ht_open(name, open_flags, dict_flags)
/* const char *name;
-/* HTABLE *table;
-/* void (*remove)(char *value)
+/* int open_flags;
+/* int dict_flags;
/* DESCRIPTION
-/* dict_ht_open() makes specified hash table accessible via the
-/* generic dictionary operations documented in dict_open(3).
-/* \fIremove\fR specifies an optional callback function
-/* that is called by the hash table manager when the hash table is
-/* removed from the dictionary manager's care. The hash table is not
-/* destroyed when \fIremove\fR is a null pointer.
+/* dict_ht_open() creates a memory-resident hash table and
+/* makes it accessible via the generic dictionary operations
+/* documented in dict_open(3). The open_flags argument is
+/* ignored.
/* SEE ALSO
/* dict(3) generic dictionary manager
/* LICENSE
#include "htable.h"
#include "dict.h"
#include "dict_ht.h"
+#include "stringops.h"
+#include "vstring.h"
/* Application-specific. */
typedef struct {
DICT dict; /* generic members */
HTABLE *table; /* hash table */
- void (*remove) (char *); /* callback */
} DICT_HT;
/* dict_ht_lookup - find hash-table entry */
dict_errno = 0;
+ /*
+ * Optionally fold the key.
+ */
+ if (dict->flags & DICT_FLAG_FOLD_FIX) {
+ if (dict->fold_buf == 0)
+ dict->fold_buf = vstring_alloc(10);
+ vstring_strcpy(dict->fold_buf, name);
+ name = lowercase(vstring_str(dict->fold_buf));
+ }
return (htable_find(dict_ht->table, name));
}
DICT_HT *dict_ht = (DICT_HT *) dict;
HTABLE_INFO *ht;
+ /*
+ * Optionally fold the key.
+ */
+ if (dict->flags & DICT_FLAG_FOLD_FIX) {
+ if (dict->fold_buf == 0)
+ dict->fold_buf = vstring_alloc(10);
+ vstring_strcpy(dict->fold_buf, name);
+ name = lowercase(vstring_str(dict->fold_buf));
+ }
if ((ht = htable_locate(dict_ht->table, name)) != 0) {
myfree(ht->value);
} else {
{
DICT_HT *dict_ht = (DICT_HT *) dict;
- if (dict_ht->remove)
- htable_free(dict_ht->table, dict_ht->remove);
+ htable_free(dict_ht->table, myfree);
dict_free(dict);
}
/* dict_ht_open - create association with hash table */
-DICT *dict_ht_open(const char *name, HTABLE *table, void (*remove) (char *))
+DICT *dict_ht_open(const char *name, int unused_open_flags, int dict_flags)
{
DICT_HT *dict_ht;
dict_ht->dict.update = dict_ht_update;
dict_ht->dict.sequence = dict_ht_sequence;
dict_ht->dict.close = dict_ht_close;
- dict_ht->table = table;
- dict_ht->remove = remove;
+ dict_ht->dict.flags = dict_flags | DICT_FLAG_FIXED;
+ if (dict_flags & DICT_FLAG_FOLD_FIX)
+ dict_ht->dict.fold_buf = vstring_alloc(10);
+ dict_ht->table = htable_create(0);
return (&dict_ht->dict);
}
*/
#define DICT_TYPE_HT "internal"
-extern DICT *dict_ht_open(const char *, HTABLE *, void (*) (char *));
+extern DICT *dict_ht_open(const char *, int, int);
/* LICENSE
/* .ad
#include <dict_regexp.h>
#include <dict_static.h>
#include <dict_cidr.h>
+#include <dict_ht.h>
#include <stringops.h>
#include <split_at.h>
#include <htable.h>
DICT_TYPE_CDB, dict_cdb_open,
#endif
DICT_TYPE_ENVIRON, dict_env_open,
+ DICT_TYPE_HT, dict_ht_open,
DICT_TYPE_UNIX, dict_unix_open,
#ifdef SNAPSHOT
DICT_TYPE_TCP, dict_tcp_open,
/* event_drain() repeatedly calls event_loop() until no more timer
/* events or I/O events are pending or until the time limit is reached.
/* This routine must not be called from an event_whatever() callback
-/* routine. Note: this function ignores pending timer events, and
-/* assumes that no new I/O events will be registered.
+/* routine. Note: this function assumes that no new I/O events
+/* will be registered.
/*
/* event_fork() must be called by a child process after it is
/* created with fork(), to re-initialize event processing.
timer = RING_TO_TIMER(ring);
if (timer->callback == callback && timer->context == context) {
timer->when = event_present + delay;
+ timer->loop_instance = event_loop_instance;
ring_detach(ring);
if (msg_verbose > 2)
msg_info("%s: reset 0x%lx 0x%lx %d", myname,
return (list);
}
-/* htable_sequence - dict_cache(3) compatibility iterator */
+/* htable_sequence - dict(3) compatibility iterator */
HTABLE_INFO *htable_sequence(HTABLE *table, int how)
{
int size; /* length of entries array */
int used; /* number of entries in table */
HTABLE_INFO **data; /* entries array, auto-resized */
- HTABLE_INFO **seq_bucket; /* current sequence bucket */
+ HTABLE_INFO **seq_bucket; /* current sequence hash bucket */
HTABLE_INFO *seq_element; /* current sequence element */
} HTABLE;
if (var_verify_scan_cache > 0) {
int expire_flags;
- expire_flags = DICT_CACHE_FLAG_EXP_SUMMARY;
+ expire_flags = DICT_CACHE_FLAG_STATISTICS;
if (msg_verbose)
- expire_flags |= DICT_CACHE_FLAG_EXP_VERBOSE;
- dict_cache_expire(verify_map, expire_flags, var_verify_scan_cache,
- verify_cache_validator,
- (char *) vstring_alloc(100));
+ expire_flags |= DICT_CACHE_FLAG_VERBOSE;
+ dict_cache_control(verify_map,
+ DICT_CACHE_CTL_FLAGS, expire_flags,
+ DICT_CACHE_CTL_INTERVAL, var_verify_scan_cache,
+ DICT_CACHE_CTL_VALIDATOR, verify_cache_validator,
+ DICT_CACHE_CTL_CONTEXT, (char *) vstring_alloc(100),
+ DICT_CACHE_CTL_END);
}
}
*/
#define VERIFY_DICT_OPEN_FLAGS (DICT_FLAG_DUP_REPLACE | DICT_FLAG_SYNC_UPDATE)
- if (*var_verify_map) {
- saved_mask = umask(022);
- verify_map =
- dict_cache_open(data_redirect_map(redirect, var_verify_map),
- O_CREAT | O_RDWR, VERIFY_DICT_OPEN_FLAGS);
- (void) umask(saved_mask);
- } else {
- verify_map =
- dict_cache_import(dict_ht_open("verify", htable_create(0), myfree));
- }
+ saved_mask = umask(022);
+ verify_map =
+ dict_cache_open(*var_verify_map ?
+ data_redirect_map(redirect, var_verify_map) :
+ "internal:verify",
+ O_CREAT | O_RDWR, VERIFY_DICT_OPEN_FLAGS);
+ (void) umask(saved_mask);
/*
* Clean up and restore privilege.