]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.5-20071203
authorWietse Venema <wietse@porcupine.org>
Mon, 8 Aug 2033 10:53:23 +0000 (05:53 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:33:32 +0000 (06:33 +0000)
26 files changed:
postfix/HISTORY
postfix/RELEASE_NOTES
postfix/conf/post-install
postfix/html/postconf.5.html
postfix/html/proxymap.8.html
postfix/man/man5/postconf.5
postfix/man/man8/proxymap.8
postfix/mantools/postlink
postfix/proto/postconf.proto
postfix/src/global/Makefile.in
postfix/src/global/dict_proxy.c
postfix/src/global/dict_proxy.h
postfix/src/global/mail_params.h
postfix/src/global/mail_proto.h
postfix/src/global/mail_version.h
postfix/src/global/mkmap.h
postfix/src/global/mkmap_open.c
postfix/src/global/mkmap_proxy.c [new file with mode: 0644]
postfix/src/oqmgr/qmgr_queue.c
postfix/src/postalias/Makefile.in
postfix/src/postalias/postalias.c
postfix/src/postmap/Makefile.in
postfix/src/postmap/postmap.c
postfix/src/proxymap/proxymap.c
postfix/src/qmgr/qmgr_queue.c
postfix/src/util/dict.h

index 50aee39bd6f822f6f51322323804b9c2a7dd67f6..815a6d9c4912b8147c649b6d3581b88a159c7013 100644 (file)
@@ -13878,3 +13878,22 @@ Apologies for any names omitted.
 
        Bugfix: don't update the back-to-back delivery time stamp
        while deferring mail. File: *qmgr/qmgr_entry.c.
+
+20071203
+
+       Feature: support for read-write tables in the proxymap
+       service. This is implemented with a separate master.cf entry
+       named "proxywrite" that should run with process limit of 1
+       if you want to update Berkeley DB like tables. This feature
+       requires that tables be authorized with the proxy_write_maps
+       configuration parameter. Files: global/dict_procy.[hc],
+       proxymap/proxymap.c.
+
+       Human factors: the postmap and postalias commands now produce
+       nicer diagnostics when asked to do something with a proxied
+       map that they can't do. Files: postmap/postmap.c,
+       postalias/postalias.c.
+
+       Bugfix: the proxymap client didn't properly propagate the
+       postmap (postalias) -r and -w options to the proxymap server.
+       File: util/dict.h.
index 60752c5c443392ebcddb48d2483cdb4a83db2862..ff9099f94850e21c8a7116af9e237ef45a508cc8 100644 (file)
@@ -17,6 +17,14 @@ Incompatibility with Postfix 2.3 and earlier
 If you upgrade from Postfix 2.3 or earlier, read RELEASE_NOTES-2.4
 before proceeding.
 
+Incompatibility with Postfix snapshot 20071203
+==============================================
+
+The "make upgrade" procedure adds a new service "proxywrite" to the
+master.cf file, for read/write lookup table access. If you copy
+your old configuration file over the updated one, you will have
+to run "postfix upgrade-configuration" again.
+
 Major changes with Postfix snapshot 20071202
 ============================================
 
index 6620d63223e43ac67b86da7b0feb4c73b920ee9a..245edfaefd09bf2691f14b717fe311294281ed0a 100644 (file)
@@ -659,6 +659,15 @@ retry     unix  -       -       n       -       -       error
 EOF
     }
 
+    # Add missing proxywrite service to master.cf.
+
+    grep '^proxywrite.*proxymap' $config_directory/master.cf >/dev/null || {
+       echo Editing $config_directory/master.cf, adding missing entry for proxywrite service
+       cat >>$config_directory/master.cf <<EOF || exit 1
+proxywrite unix -       -       n       -       1       proxymap
+EOF
+    }
+
     # Report (but do not remove) obsolete files.
 
     test -n "$obsolete" && {
index b81212e4cb088b3e6f59b708d6b013a87f1a94e1..cc7f6d06dba82760f20a4b79076e82173e2b730d 100644 (file)
@@ -6152,9 +6152,9 @@ Example:
 (default: see "postconf -d" output)</b></DT><DD>
 
 <p>
-The lookup tables that the <a href="proxymap.8.html">proxymap(8)</a> server is allowed to access.
-Table references that don't begin with <a href="proxymap.8.html">proxy</a>: are ignored.  The
-<a href="proxymap.8.html">proxymap(8)</a> table accesses are read-only.
+The lookup tables that the <a href="proxymap.8.html">proxymap(8)</a> server is allowed to
+access for the read-only service.
+Table references that don't begin with <a href="proxymap.8.html">proxy</a>: are ignored.
 </p>
 
 <p>
@@ -6162,6 +6162,22 @@ This feature is available in Postfix 2.0 and later.
 </p>
 
 
+</DD>
+
+<DT><b><a name="proxy_write_maps">proxy_write_maps</a>
+(default: see "postconf -d" output)</b></DT><DD>
+
+<p>
+The lookup tables that the <a href="proxymap.8.html">proxymap(8)</a> server is allowed to
+access for the read-write service.
+Table references that don't begin with <a href="proxymap.8.html">proxy</a>: are ignored.
+</p>
+
+<p>
+This feature is available in Postfix 2.5 and later.
+</p>
+
+
 </DD>
 
 <DT><b><a name="qmgr_clog_warn_time">qmgr_clog_warn_time</a>
index f3a5270b6fb99fbc3f591d00c73fc28e2dee44a2..11c11055dc807f4b10f2de453ecc046f3859d7ce 100644 (file)
@@ -13,8 +13,10 @@ PROXYMAP(8)                                                        PROXYMAP(8)
        <b>proxymap</b> [generic Postfix daemon options]
 
 <b>DESCRIPTION</b>
-       The  <a href="proxymap.8.html"><b>proxymap</b>(8)</a>  server  provides  read-only table lookup
-       service to Postfix processes. The purpose of  the  service
+       The  <a href="proxymap.8.html"><b>proxymap</b>(8)</a>  server  provides read-only or read-write
+       table lookup service to Postfix processes. These  services
+       are  implemented with distinct service names: <b>proxymap</b> and
+       <b>proxywrite</b>, respectively. The purpose  of  these  services
        is:
 
        <b>o</b>      To  overcome  chroot  restrictions.  For example, a
@@ -39,6 +41,10 @@ PROXYMAP(8)                                                        PROXYMAP(8)
               The total number of connections is limited  by  the
               number of proxymap server processes.
 
+       <b>o</b>      To  provide single-updater functionality for lookup
+              tables that do not reliably support multiple  writ-
+              ers (i.e. all file-based tables).
+
        The <a href="proxymap.8.html"><b>proxymap</b>(8)</a> server implements the following requests:
 
        <b>open</b> <i>maptype:mapname flags</i>
@@ -49,10 +55,26 @@ PROXYMAP(8)                                                        PROXYMAP(8)
 
        <b>lookup</b> <i>maptype:mapname flags key</i>
               Look up the data stored under  the  requested  key.
-              The  reply  is  the  request completion status code
-              (below) and the  lookup  result  value.   The  <i>map-</i>
-              <i>type:mapname</i>  and  <i>flags</i>  are  the same as with the
-              <b>open</b> request.
+              The reply is the request completion status code and
+              the lookup result value.  The  <i>maptype:mapname</i>  and
+              <i>flags</i> are the same as with the <b>open</b> request.
+
+       <b>update</b> <i>maptype:mapname flags key value</i>
+              Update  the  data  stored  under the requested key.
+              The reply is the request  completion  status  code.
+              The  <i>maptype:mapname</i> and <i>flags</i> are the same as with
+              the <b>open</b> request.
+
+              To implement single-updater maps, specify a process
+              limit  of  1  in  the  <a href="master.5.html">master.cf</a> file entry for the
+              proxywrite service.
+
+              This request is supported in Postfix 2.5 and later.
+
+       The  request  completion status is one of OK, RETRY, NOKEY
+       (lookup failed because the key was not found),  BAD  (mal-
+       formed  request)  or  DENY  (the table is not approved for
+       proxy read or update access).
 
        There is no  <b>close</b>  command,  nor  are  tables  implicitly
        closed  when a client disconnects. The purpose is to share
@@ -69,11 +91,11 @@ PROXYMAP(8)                                                        PROXYMAP(8)
 
 <b>SECURITY</b>
        The <a href="proxymap.8.html"><b>proxymap</b>(8)</a> server opens only tables that are approved
-       via  the <b><a href="postconf.5.html#proxy_read_maps">proxy_read_maps</a></b> configuration parameter, does not
-       talk to  users,  and  can  run  at  fixed  low  privilege,
-       chrooted  or  not.   However,  running the proxymap server
-       chrooted severely limits usability, because  it  can  open
-       only chrooted tables.
+       via  the <b><a href="postconf.5.html#proxy_read_maps">proxy_read_maps</a></b> or <b><a href="postconf.5.html#proxy_write_maps">proxy_write_maps</a></b> configuration
+       parameters, does not talk to users, and can run  at  fixed
+       low  privilege,  chrooted  or  not.   However, running the
+       proxymap  server  chrooted  severely   limits   usability,
+       because it can open only chrooted tables.
 
        The  <a href="proxymap.8.html"><b>proxymap</b>(8)</a>  server  is not a trusted daemon process,
        and must not be used to look up sensitive information such
@@ -94,6 +116,16 @@ PROXYMAP(8)                                                        PROXYMAP(8)
        clients,  and  must  therefore not be used for tables that
        have high-latency lookups.
 
+       The <a href="proxymap.8.html"><b>proxymap</b>(8)</a> read-write  service  does  not  explicitly
+       close  lookup  tables  (even  if it did, this could not be
+       relied on, because the process may be  terminated  between
+       table  updates).   The read-write service should therefore
+       not be used with tables that leave persistent  storage  in
+       an  inconsistent state between updates (for example, CDB).
+       Tables that support "sync on update" should be  safe  (for
+       example,  Berkeley  DB)  as  should tables that are imple-
+       mented by a real DBMS.
+
 <b>CONFIGURATION PARAMETERS</b>
        On busy mail systems a long time  may  pass  before  <a href="proxymap.8.html"><b>prox-</b></a>
        <a href="proxymap.8.html"><b>ymap</b>(8)</a> relevant changes to <a href="postconf.5.html"><b>main.cf</b></a> are picked up. Use the
@@ -135,7 +167,13 @@ PROXYMAP(8)                                                        PROXYMAP(8)
 
        <b><a href="postconf.5.html#proxy_read_maps">proxy_read_maps</a> (see 'postconf -d' output)</b>
               The  lookup  tables  that the <a href="proxymap.8.html"><b>proxymap</b>(8)</a> server is
-              allowed to access.
+              allowed to access for the read-only service.
+
+       Available in Postfix 2.5 and later:
+
+       <b><a href="postconf.5.html#proxy_write_maps">proxy_write_maps</a> (see 'postconf -d' output)</b>
+              The lookup tables that the  <a href="proxymap.8.html"><b>proxymap</b>(8)</a>  server  is
+              allowed to access for the read-write service.
 
 <b>SEE ALSO</b>
        <a href="postconf.5.html">postconf(5)</a>, configuration parameters
@@ -145,7 +183,7 @@ PROXYMAP(8)                                                        PROXYMAP(8)
        <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>HISTORY</b>
index 0620f2267bb3e5d6bc5f841bdb5853efa5ce749e..c14d6a85038b53afaf5e6ffc0c0c4b48e6decbfa 100644 (file)
@@ -3423,11 +3423,17 @@ proxy_interfaces = 1.2.3.4
 .ad
 .ft R
 .SH proxy_read_maps (default: see "postconf -d" output)
-The lookup tables that the \fBproxymap\fR(8) server is allowed to access.
-Table references that don't begin with proxy: are ignored.  The
-\fBproxymap\fR(8) table accesses are read-only.
+The lookup tables that the \fBproxymap\fR(8) server is allowed to
+access for the read-only service.
+Table references that don't begin with proxy: are ignored.
 .PP
 This feature is available in Postfix 2.0 and later.
+.SH proxy_write_maps (default: see "postconf -d" output)
+The lookup tables that the \fBproxymap\fR(8) server is allowed to
+access for the read-write service.
+Table references that don't begin with proxy: are ignored.
+.PP
+This feature is available in Postfix 2.5 and later.
 .SH qmgr_clog_warn_time (default: 300s)
 The minimal delay between warnings that a specific destination is
 clogging up the Postfix active queue. Specify 0 to disable.
index 3e31515c51f5ad09a1a6aff0328a865f7b67873b..74a161c162a0381a9bd6efe1afee2fec5c912803 100644 (file)
@@ -12,9 +12,10 @@ Postfix lookup table proxy server
 .SH DESCRIPTION
 .ad
 .fi
-The \fBproxymap\fR(8) server provides read-only table
-lookup service to Postfix processes. The purpose
-of the service is:
+The \fBproxymap\fR(8) server provides read-only or read-write
+table lookup service to Postfix processes. These services are
+implemented with distinct service names: \fBproxymap\fR and
+\fBproxywrite\fR, respectively. The purpose of these services is:
 .IP \(bu
 To overcome chroot restrictions. For example, a chrooted SMTP
 server needs access to the system passwd file in order to
@@ -39,6 +40,10 @@ virtual_alias_maps =
 .sp
 The total number of connections is limited by the number of
 proxymap server processes.
+.IP \(bu
+To provide single-updater functionality for lookup tables
+that do not reliably support multiple writers (i.e. all
+file-based tables).
 .PP
 The \fBproxymap\fR(8) server implements the following requests:
 .IP "\fBopen\fR \fImaptype:mapname flags\fR"
@@ -48,11 +53,26 @@ dependent flags (to distinguish a fixed string table from a regular
 expression table).
 .IP "\fBlookup\fR \fImaptype:mapname flags key\fR"
 Look up the data stored under the requested key.
-The reply is the request completion status code (below) and
+The reply is the request completion status code and
 the lookup result value.
 The \fImaptype:mapname\fR and \fIflags\fR are the same
 as with the \fBopen\fR request.
+.IP "\fBupdate\fR \fImaptype:mapname flags key value\fR"
+Update the data stored under the requested key.
+The reply is the request completion status code.
+The \fImaptype:mapname\fR and \fIflags\fR are the same
+as with the \fBopen\fR request.
+.sp
+To implement single-updater maps, specify a process limit
+of 1 in the master.cf file entry for the proxywrite service.
+.sp
+This request is supported in Postfix 2.5 and later.
 .PP
+The request completion status is one of OK, RETRY, NOKEY
+(lookup failed because the key was not found), BAD (malformed
+request) or DENY (the table is not approved for proxy read
+or update access).
+
 There is no \fBclose\fR command, nor are tables implicitly closed
 when a client disconnects. The purpose is to share tables among
 multiple client processes.
@@ -74,8 +94,9 @@ or after \fB$max_idle\fR seconds of idle time.
 .nf
 .ad
 .fi
-The \fBproxymap\fR(8) server opens only tables that are approved via the
-\fBproxy_read_maps\fR configuration parameter, does not talk to
+The \fBproxymap\fR(8) server opens only tables that are
+approved via the \fBproxy_read_maps\fR or \fBproxy_write_maps\fR
+configuration parameters, does not talk to
 users, and can run at fixed low privilege, chrooted or not.
 However, running the proxymap server chrooted severely limits
 usability, because it can open only chrooted tables.
@@ -98,6 +119,15 @@ Problems and transactions are logged to \fBsyslogd\fR(8).
 The \fBproxymap\fR(8) server provides service to multiple clients,
 and must therefore not be used for tables that have high-latency
 lookups.
+
+The \fBproxymap\fR(8) read-write service does not explicitly
+close lookup tables (even if it did, this could not be relied on,
+because the process may be terminated between table updates).
+The read-write service should therefore not be used with tables that
+leave persistent storage in an inconsistent state between
+updates (for example, CDB). Tables that support "sync on
+update" should be safe (for example, Berkeley DB) as should
+tables that are implemented by a real DBMS.
 .SH "CONFIGURATION PARAMETERS"
 .na
 .nf
@@ -130,7 +160,13 @@ The process ID of a Postfix command or daemon process.
 .IP "\fBprocess_name (read-only)\fR"
 The process name of a Postfix command or daemon process.
 .IP "\fBproxy_read_maps (see 'postconf -d' output)\fR"
-The lookup tables that the \fBproxymap\fR(8) server is allowed to access.
+The lookup tables that the \fBproxymap\fR(8) server is allowed to
+access for the read-only service.
+.PP
+Available in Postfix 2.5 and later:
+.IP "\fBproxy_write_maps (see 'postconf -d' output)\fR"
+The lookup tables that the \fBproxymap\fR(8) server is allowed to
+access for the read-write service.
 .SH "SEE ALSO"
 .na
 .nf
index f4e9b44091e58d58a4bdcec72b9cb5eff2eeb869..1812212c37a8acc83eff41575a7286e10221c075 100755 (executable)
@@ -328,6 +328,7 @@ while (<>) {
     s;\bpropagate_unmatched_extensions\b;<a href="postconf.5.html#propagate_unmatched_extensions">$&</a>;g;
     s;\bproxy_inter[-</bB>]*\n* *[<bB>]*faces\b;<a href="postconf.5.html#proxy_interfaces">$&</a>;g;
     s;\bproxy_read_maps\b;<a href="postconf.5.html#proxy_read_maps">$&</a>;g;
+    s;\bproxy_write_maps\b;<a href="postconf.5.html#proxy_write_maps">$&</a>;g;
     s;\bqmgr_clog_warn_time\b;<a href="postconf.5.html#qmgr_clog_warn_time">$&</a>;g;
     s;\bqmgr_fudge_factor\b;<a href="postconf.5.html#qmgr_fudge_factor">$&</a>;g;
     s;\bqmgr_message_active_limit\b;<a href="postconf.5.html#qmgr_message_active_limit">$&</a>;g;
index f5f1771eb2407730e1e0766d91e7ad53677bd31c..d5002584fb66d85f02c7f524f3402845516e8d68 100644 (file)
@@ -7204,15 +7204,27 @@ This is a read-only parameter.
 %PARAM proxy_read_maps see "postconf -d" output
 
 <p>
-The lookup tables that the proxymap(8) server is allowed to access.
-Table references that don't begin with proxy: are ignored.  The
-proxymap(8) table accesses are read-only.
+The lookup tables that the proxymap(8) server is allowed to 
+access for the read-only service.
+Table references that don't begin with proxy: are ignored.
 </p>
 
 <p>
 This feature is available in Postfix 2.0 and later.
 </p>
 
+%PARAM proxy_write_maps see "postconf -d" output
+
+<p>
+The lookup tables that the proxymap(8) server is allowed to 
+access for the read-write service.
+Table references that don't begin with proxy: are ignored.
+</p>
+
+<p>
+This feature is available in Postfix 2.5 and later.
+</p>
+
 %PARAM qmgr_clog_warn_time 300s
 
 <p>
index e354ed69e767b789db2d6cd6cfb10b677e398dbc..b87e7733562b9be997328c059ba16c0b2e348451 100644 (file)
@@ -28,7 +28,7 @@ SRCS  = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \
        tok822_resolve.c tok822_rewrite.c tok822_tree.c trace.c \
        user_acl.c valid_mailhost_addr.c verify.c verify_clnt.c \
        verp_sender.c wildcard_inet_addr.c xtext.c delivered_hdr.c \
-       fold_addr.c header_body_checks.c
+       fold_addr.c header_body_checks.c mkmap_proxy.c
 OBJS   = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
        canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \
        clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \
@@ -58,7 +58,7 @@ OBJS  = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
        tok822_resolve.o tok822_rewrite.o tok822_tree.o trace.o \
        user_acl.o valid_mailhost_addr.o verify.o verify_clnt.o \
        verp_sender.o wildcard_inet_addr.o xtext.o delivered_hdr.o \
-       fold_addr.o header_body_checks.o
+       fold_addr.o header_body_checks.o mkmap_proxy.o
 HDRS   = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
        canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \
        conv_time.h db_common.h debug_peer.h debug_process.h defer.h \
@@ -1451,8 +1451,19 @@ mkmap_open.o: ../../include/sys_defs.h
 mkmap_open.o: ../../include/vbuf.h
 mkmap_open.o: ../../include/vstream.h
 mkmap_open.o: ../../include/vstring.h
+mkmap_open.o: dict_proxy.h
 mkmap_open.o: mkmap.h
 mkmap_open.o: mkmap_open.c
+mkmap_proxy.o: ../../include/argv.h
+mkmap_proxy.o: ../../include/dict.h
+mkmap_proxy.o: ../../include/mymalloc.h
+mkmap_proxy.o: ../../include/sys_defs.h
+mkmap_proxy.o: ../../include/vbuf.h
+mkmap_proxy.o: ../../include/vstream.h
+mkmap_proxy.o: ../../include/vstring.h
+mkmap_proxy.o: dict_proxy.h
+mkmap_proxy.o: mkmap.h
+mkmap_proxy.o: mkmap_proxy.c
 mkmap_sdbm.o: ../../include/argv.h
 mkmap_sdbm.o: ../../include/dict.h
 mkmap_sdbm.o: ../../include/dict_sdbm.h
index 264b5ed397d28c743ca5413d8e02053343eff202..e4f7884732b5bbcb7a06c9ae5bb1424fcf6ce205 100644 (file)
 /*     dict_proxy_open() relays read-only operations through
 /*     the Postfix proxymap server.
 /*
-/*     The \fIopen_flags\fR argument must specify O_RDONLY.
+/*     The \fIopen_flags\fR argument must specify O_RDONLY
+/*     or O_RDWR|O_CREAT. Depending on this, the client
+/*     connects to the proxymap multiserver or to the
+/*     proxywrite single updater.
 /*
 /*     The connection to the Postfix proxymap server is automatically
 /*     closed after $ipc_idle seconds of idle time, or after $ipc_ttl
@@ -154,6 +157,69 @@ static const char *dict_proxy_lookup(DICT *dict, const char *key)
     }
 }
 
+/* dict_proxy_update - update table entry */
+
+static void dict_proxy_update(DICT *dict, const char *key, const char *value)
+{
+    const char *myname = "dict_proxy_update";
+    DICT_PROXY *dict_proxy = (DICT_PROXY *) dict;
+    VSTREAM *stream;
+    int     status;
+    int     count = 0;
+    int     request_flags;
+
+    /*
+     * The client and server live in separate processes that may start and
+     * terminate independently. We cannot rely on a persistent connection,
+     * let alone on persistent state (such as a specific open table) that is
+     * associated with a specific connection. Each lookup needs to specify
+     * the table and the flags that were specified to dict_proxy_open().
+     */
+    request_flags = (dict_proxy->in_flags & DICT_FLAG_RQST_MASK)
+       | (dict->flags & DICT_FLAG_RQST_MASK);
+    for (;;) {
+       stream = clnt_stream_access(proxy_stream);
+       errno = 0;
+       count += 1;
+       if (attr_print(stream, ATTR_FLAG_NONE,
+                      ATTR_TYPE_STR, MAIL_ATTR_REQ, PROXY_REQ_UPDATE,
+                      ATTR_TYPE_STR, MAIL_ATTR_TABLE, dict->name,
+                      ATTR_TYPE_INT, MAIL_ATTR_FLAGS, request_flags,
+                      ATTR_TYPE_STR, MAIL_ATTR_KEY, key,
+                      ATTR_TYPE_STR, MAIL_ATTR_VALUE, value,
+                      ATTR_TYPE_END) != 0
+           || vstream_fflush(stream)
+           || attr_scan(stream, ATTR_FLAG_STRICT,
+                        ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
+                        ATTR_TYPE_END) != 1) {
+           if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
+               msg_warn("%s: service %s: %m", myname, VSTREAM_PATH(stream));
+       } else {
+           if (msg_verbose)
+               msg_info("%s: table=%s flags=%s key=%s value=%s -> status=%d",
+                        myname, dict->name, dict_flags_str(request_flags), 
+                        key, value, status);
+           switch (status) {
+           case PROXY_STAT_BAD:
+               msg_fatal("%s lookup failed for table \"%s\" key \"%s\": "
+                         "invalid request",
+                         MAIL_SERVICE_PROXYMAP, dict->name, key);
+           case PROXY_STAT_DENY:
+               msg_fatal("%s update access is not configured for table \"%s\"",
+                         MAIL_SERVICE_PROXYMAP, dict->name);
+           case PROXY_STAT_OK:
+               return;
+           default:
+               msg_warn("%s update failed for table \"%s\" key \"%s\": "
+                        "unexpected reply status %d",
+                        MAIL_SERVICE_PROXYMAP, dict->name, key, status);
+           }
+       }
+       clnt_stream_recover(proxy_stream);
+       sleep(1);                               /* XXX make configurable */
+    }
+}
+
 /* dict_proxy_close - disconnect */
 
 static void dict_proxy_close(DICT *dict)
@@ -178,9 +244,14 @@ DICT   *dict_proxy_open(const char *map, int open_flags, int dict_flags)
 
     /*
      * Sanity checks.
+     * 
+     * XXX A complete implementation would also allow O_RDWR without O_CREAT.
+     * But we must not pass on every possible set of flags to the proxy
+     * server; only sets that make sense. For now, the flags are passed
+     * implicitly by choosing between the proxymap or proxywrite service.
      */
-    if (open_flags != O_RDONLY)
-       msg_fatal("%s: %s map open requires O_RDONLY access mode",
+    if (open_flags != O_RDONLY && open_flags != (O_RDWR | O_CREAT))
+       msg_fatal("%s: %s map open requires O_RDONLY or O_RDWR|O_CREAT mode",
                  map, DICT_TYPE_PROXY);
 
     /*
@@ -197,6 +268,7 @@ DICT   *dict_proxy_open(const char *map, int open_flags, int dict_flags)
     dict_proxy = (DICT_PROXY *)
        dict_alloc(DICT_TYPE_PROXY, map, sizeof(*dict_proxy));
     dict_proxy->dict.lookup = dict_proxy_lookup;
+    dict_proxy->dict.update = dict_proxy_update;
     dict_proxy->dict.close = dict_proxy_close;
     dict_proxy->in_flags = dict_flags;
     dict_proxy->result = vstring_alloc(10);
@@ -207,13 +279,18 @@ DICT   *dict_proxy_open(const char *map, int open_flags, int dict_flags)
      * XXX Use absolute pathname to make this work from non-daemon processes.
      */
     if (proxy_stream == 0) {
-       if (access(MAIL_CLASS_PRIVATE "/" MAIL_SERVICE_PROXYMAP, F_OK) == 0)
+       if (access(open_flags == O_RDONLY ?
+                  MAIL_CLASS_PRIVATE "/" MAIL_SERVICE_PROXYMAP :
+                  MAIL_CLASS_PRIVATE "/" MAIL_SERVICE_PROXYWRITE,
+                  F_OK) == 0)
            prefix = MAIL_CLASS_PRIVATE;
        else
            prefix = kludge = concatenate(var_queue_dir, "/",
                                          MAIL_CLASS_PRIVATE, (char *) 0);
        proxy_stream = clnt_stream_create(prefix,
-                                         MAIL_SERVICE_PROXYMAP,
+                                         open_flags == O_RDONLY ?
+                                         MAIL_SERVICE_PROXYMAP :
+                                         MAIL_SERVICE_PROXYWRITE,
                                          var_ipc_idle_limit,
                                          var_ipc_ttl_limit);
        if (kludge)
index ce49626094936479a69c9dbb696bc5ec0a99c8da..2a1e3fd218ff3be3ce68fc725590be35fbcb0758 100644 (file)
@@ -28,6 +28,7 @@ extern DICT *dict_proxy_open(const char *, int, int);
   */
 #define PROXY_REQ_OPEN         "open"
 #define PROXY_REQ_LOOKUP       "lookup"
+#define PROXY_REQ_UPDATE       "update"
 
 #define PROXY_STAT_OK          0       /* operation succeeded */
 #define PROXY_STAT_NOKEY       1       /* requested key not found */
index cb46de241d0d1fb058a8c8a6007bf117d23e0e8f..ca7679f47c8d384ce06620b8f1c8cbb95ee68474 100644 (file)
@@ -2014,6 +2014,10 @@ extern int var_local_rcpt_code;
                                " $" VAR_MYNETWORKS
 extern char *var_proxy_read_maps;
 
+#define VAR_PROXY_WRITE_MAPS   "proxy_write_maps"
+#define DEF_PROXY_WRITE_MAPS   ""      /* Add here: "$" VAR_AUTH_FAIL_MAP */
+extern char *var_proxy_write_maps;
+
  /*
   * Other.
   */
index 89b16883b6cc5ef8e6f22801ef7e1905e2eeea2c..71b5779ef462440f88db1217f7d309276bf90d0a 100644 (file)
@@ -56,6 +56,7 @@
 #define MAIL_SERVICE_TRACE     "trace"
 #define MAIL_SERVICE_RELAY     "relay"
 #define MAIL_SERVICE_PROXYMAP  "proxymap"
+#define MAIL_SERVICE_PROXYWRITE        "proxywrite"
 #define MAIL_SERVICE_SCACHE    "scache"
 
  /*
index 0ea521443166e18fc2a5fc9e4fca4c022cb28fe0..dd540c643823b25b4f789c37891794986da084b7 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20071130"
+#define MAIL_RELEASE_DATE      "2007111203"
 #define MAIL_VERSION_NUMBER    "2.5"
 
 #ifdef SNAPSHOT
index 45e32c69c2837d9c7150d3681778ba6d9f3a7650..969b2a733aa3fb9d4430d1d90d7db4b3e0c4c923 100644 (file)
@@ -40,6 +40,7 @@ extern MKMAP *mkmap_cdb_open(const char *);
 extern MKMAP *mkmap_hash_open(const char *);
 extern MKMAP *mkmap_btree_open(const char *);
 extern MKMAP *mkmap_sdbm_open(const char *);
+extern MKMAP *mkmap_proxy_open(const char *);
 
 /* LICENSE
 /* .ad
index a3caf952fdc19f725bdeb6e57376c9f65ad8bfce..f96cd472efba7c1ccb3115e8df0ea39231194007 100644 (file)
@@ -66,6 +66,7 @@
 #include <dict_cdb.h>
 #include <dict_dbm.h>
 #include <dict_sdbm.h>
+#include <dict_proxy.h>
 #include <sigdelay.h>
 #include <mymalloc.h>
 
@@ -83,6 +84,7 @@ typedef struct {
 } MKMAP_OPEN_INFO;
 
 MKMAP_OPEN_INFO mkmap_types[] = {
+    DICT_TYPE_PROXY, mkmap_proxy_open,
 #ifdef HAS_CDB
     DICT_TYPE_CDB, mkmap_cdb_open,
 #endif
diff --git a/postfix/src/global/mkmap_proxy.c b/postfix/src/global/mkmap_proxy.c
new file mode 100644 (file)
index 0000000..e4f4f34
--- /dev/null
@@ -0,0 +1,58 @@
+/*++
+/* NAME
+/*     mkmap_proxy 3
+/* SUMMARY
+/*     create or proxied database
+/* SYNOPSIS
+/*     #include <mkmap.h>
+/*
+/*     MKMAP   *mkmap_proxy_open(path)
+/*     const char *path;
+/* DESCRIPTION
+/*     This module implements support for updating proxy databases.
+/*
+/*     mkmap_proxy_open() is a proxymap-specific helper for the
+/*     more general mkmap_open() routine.
+/*
+/*     All errors are fatal.
+/* SEE ALSO
+/*     dict_proxy(3), proxy client interface.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <dict_proxy.h>
+
+/* Application-specific. */
+
+#include "mkmap.h"
+
+/* mkmap_proxy_open - create or open database */
+
+MKMAP  *mkmap_proxy_open(const char *unused_path)
+{
+    MKMAP *mkmap = (MKMAP *) mymalloc(sizeof(*mkmap));
+
+    /*
+     * Fill in the generic members.
+     */
+    mkmap->open = dict_proxy_open;
+    mkmap->after_open = 0;
+    mkmap->after_close = 0;
+
+    return (mkmap);
+}
index 22bf0921cad3750f17f62a09fb9ef31f80dd1ea8..ae3de84c20d1c072f02aad6becefc8dd4680cc3e 100644 (file)
@@ -304,7 +304,7 @@ void    qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
        queue->fail_cohorts += 1.0 / queue->window;
        if (transport->fail_cohort_limit > 0
            && queue->fail_cohorts >= transport->fail_cohort_limit)
-           queue->window = 0;
+           queue->window = QMGR_QUEUE_STAT_THROTTLED;
     }
 
     /*
index 2e748382ea90fa0c33eadf9f2befb03ec6a44822..a10bdd1f71e5e99f021b0940ce9b49e3698fc756 100644 (file)
@@ -83,6 +83,7 @@ depend: $(MAKES)
 # do not edit below this line - it is generated by 'make depend'
 postalias.o: ../../include/argv.h
 postalias.o: ../../include/dict.h
+postalias.o: ../../include/dict_proxy.h
 postalias.o: ../../include/mail_conf.h
 postalias.o: ../../include/mail_dict.h
 postalias.o: ../../include/mail_params.h
index 955116b995b4b2047abc434e07967d5ac9c02dec..186601f56af86e8f2e6c3e68ce6600f5ca691605 100644 (file)
 #include <mail_version.h>
 #include <mkmap.h>
 #include <mail_task.h>
+#include <dict_proxy.h>
 
 /* Application-specific. */
 
@@ -275,6 +276,8 @@ static void postalias(char *map_type, char *path_name, int postalias_flags,
     if ((open_flags & O_TRUNC) == 0) {
        source_fp = VSTREAM_IN;
        vstream_control(source_fp, VSTREAM_CTL_PATH, "stdin", VSTREAM_CTL_END);
+    } else if (strcmp(map_type, DICT_TYPE_PROXY) == 0) {
+       msg_fatal("can't create maps via the proxy service");
     } else if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0) {
        msg_fatal("open %s: %m", path_name);
     }
@@ -524,10 +527,14 @@ static int postalias_deletes(VSTREAM *in, char **maps, const int map_count,
      * Open maps ahead of time.
      */
     dicts = (DICT **) mymalloc(sizeof(*dicts) * map_count);
-    for (n = 0; n < map_count; n++)
-       dicts[n] = ((map_name = split_at(maps[n], ':')) != 0 ?
+    for (n = 0; n < map_count; n++) {
+       map_name = split_at(maps[n], ':');
+       if (map_name && strcmp(maps[n], DICT_TYPE_PROXY) == 0)
+           msg_fatal("can't delete map entries via the proxy service");
+       dicts[n] = (map_name != 0 ?
                    dict_open3(maps[n], map_name, O_RDWR, dict_flags) :
                    dict_open3(var_db_type, maps[n], O_RDWR, dict_flags));
+    }
 
     /*
      * Perform all requests.
@@ -556,6 +563,8 @@ static int postalias_delete(const char *map_type, const char *map_name,
     DICT   *dict;
     int     status;
 
+    if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
+       msg_fatal("can't delete map entries via the proxy service");
     dict = dict_open3(map_type, map_name, O_RDWR, dict_flags);
     status = dict_del(dict, key);
     dict_close(dict);
@@ -572,6 +581,8 @@ static void postalias_seq(const char *map_type, const char *map_name,
     const char *value;
     int     func;
 
+    if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
+       msg_fatal("can't sequence maps via the proxy service");
     dict = dict_open3(map_type, map_name, O_RDONLY, dict_flags);
     for (func = DICT_SEQ_FUN_FIRST; /* void */ ; func = DICT_SEQ_FUN_NEXT) {
        if (dict_seq(dict, func, &key, &value) != 0)
index 508fae4eca670b75af9e8dd74e1099029d040bde..3ff41f6724b56989b8319898fcd5da6047c6c5d2 100644 (file)
@@ -83,6 +83,7 @@ depend: $(MAKES)
 # do not edit below this line - it is generated by 'make depend'
 postmap.o: ../../include/argv.h
 postmap.o: ../../include/dict.h
+postmap.o: ../../include/dict_proxy.h
 postmap.o: ../../include/mail_conf.h
 postmap.o: ../../include/mail_dict.h
 postmap.o: ../../include/mail_params.h
index 0f09630bbb68c7bc16224abb3cb882ec3bec7518..2631c7e455bd39821cf7f341b6b27fcd936cb964 100644 (file)
 #include <mail_version.h>
 #include <mkmap.h>
 #include <mail_task.h>
+#include <dict_proxy.h>
 
 /* Application-specific. */
 
@@ -279,6 +280,8 @@ static void postmap(char *map_type, char *path_name, int postmap_flags,
     if ((open_flags & O_TRUNC) == 0) {
        source_fp = VSTREAM_IN;
        vstream_control(source_fp, VSTREAM_CTL_PATH, "stdin", VSTREAM_CTL_END);
+    } else if (strcmp(map_type, DICT_TYPE_PROXY) == 0) {
+       msg_fatal("can't create maps via the proxy service");
     } else if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0) {
        msg_fatal("open %s: %m", path_name);
     }
@@ -471,10 +474,14 @@ static int postmap_deletes(VSTREAM *in, char **maps, const int map_count,
      * Open maps ahead of time.
      */
     dicts = (DICT **) mymalloc(sizeof(*dicts) * map_count);
-    for (n = 0; n < map_count; n++)
-       dicts[n] = ((map_name = split_at(maps[n], ':')) != 0 ?
+    for (n = 0; n < map_count; n++) {
+       map_name = split_at(maps[n], ':');
+       if (map_name && strcmp(maps[n], DICT_TYPE_PROXY) == 0)
+           msg_fatal("can't delete map entries via the proxy service");
+       dicts[n] = (map_name != 0 ?
                    dict_open3(maps[n], map_name, O_RDWR, dict_flags) :
                    dict_open3(var_db_type, maps[n], O_RDWR, dict_flags));
+    }
 
     /*
      * Perform all requests.
@@ -503,6 +510,8 @@ static int postmap_delete(const char *map_type, const char *map_name,
     DICT   *dict;
     int     status;
 
+    if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
+       msg_fatal("can't delete map entries via the proxy service");
     dict = dict_open3(map_type, map_name, O_RDWR, dict_flags);
     status = dict_del(dict, key);
     dict_close(dict);
@@ -519,6 +528,8 @@ static void postmap_seq(const char *map_type, const char *map_name,
     const char *value;
     int     func;
 
+    if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
+       msg_fatal("can't sequence maps via the proxy service");
     dict = dict_open3(map_type, map_name, O_RDONLY, dict_flags);
     for (func = DICT_SEQ_FUN_FIRST; /* void */ ; func = DICT_SEQ_FUN_NEXT) {
        if (dict_seq(dict, func, &key, &value) != 0)
index a4412058e3e70fa74ccc9fe63aab3ab0023b6f04..a23a310312a91879f1d874dd844f8d78a4b2ec1f 100644 (file)
@@ -6,9 +6,10 @@
 /* SYNOPSIS
 /*     \fBproxymap\fR [generic Postfix daemon options]
 /* DESCRIPTION
-/*     The \fBproxymap\fR(8) server provides read-only table
-/*     lookup service to Postfix processes. The purpose
-/*     of the service is:
+/*     The \fBproxymap\fR(8) server provides read-only or read-write
+/*     table lookup service to Postfix processes. These services are
+/*     implemented with distinct service names: \fBproxymap\fR and
+/*     \fBproxywrite\fR, respectively. The purpose of these services is:
 /* .IP \(bu
 /*     To overcome chroot restrictions. For example, a chrooted SMTP
 /*     server needs access to the system passwd file in order to
 /* .sp
 /*     The total number of connections is limited by the number of
 /*     proxymap server processes.
+/* .IP \(bu
+/*     To provide single-updater functionality for lookup tables
+/*     that do not reliably support multiple writers (i.e. all
+/*     file-based tables).
 /* .PP
 /*     The \fBproxymap\fR(8) server implements the following requests:
 /* .IP "\fBopen\fR \fImaptype:mapname flags\fR"
 /*     expression table).
 /* .IP "\fBlookup\fR \fImaptype:mapname flags key\fR"
 /*     Look up the data stored under the requested key.
-/*     The reply is the request completion status code (below) and
+/*     The reply is the request completion status code and
 /*     the lookup result value.
 /*     The \fImaptype:mapname\fR and \fIflags\fR are the same
 /*     as with the \fBopen\fR request.
+/* .IP "\fBupdate\fR \fImaptype:mapname flags key value\fR"
+/*     Update the data stored under the requested key.
+/*     The reply is the request completion status code.
+/*     The \fImaptype:mapname\fR and \fIflags\fR are the same
+/*     as with the \fBopen\fR request.
+/* .sp
+/*     To implement single-updater maps, specify a process limit
+/*     of 1 in the master.cf file entry for the proxywrite service.
+/* .sp
+/*     This request is supported in Postfix 2.5 and later.
 /* .PP
+/*     The request completion status is one of OK, RETRY, NOKEY
+/*     (lookup failed because the key was not found), BAD (malformed
+/*     request) or DENY (the table is not approved for proxy read
+/*     or update access).
+/*
 /*     There is no \fBclose\fR command, nor are tables implicitly closed
 /*     when a client disconnects. The purpose is to share tables among
 /*     multiple client processes.
@@ -64,8 +84,9 @@
 /* SECURITY
 /* .ad
 /* .fi
-/*     The \fBproxymap\fR(8) server opens only tables that are approved via the
-/*     \fBproxy_read_maps\fR configuration parameter, does not talk to
+/*     The \fBproxymap\fR(8) server opens only tables that are
+/*     approved via the \fBproxy_read_maps\fR or \fBproxy_write_maps\fR
+/*     configuration parameters, does not talk to
 /*     users, and can run at fixed low privilege, chrooted or not.
 /*     However, running the proxymap server chrooted severely limits
 /*     usability, because it can open only chrooted tables.
 /*     The \fBproxymap\fR(8) server provides service to multiple clients,
 /*     and must therefore not be used for tables that have high-latency
 /*     lookups.
+/*
+/*     The \fBproxymap\fR(8) read-write service does not explicitly
+/*     close lookup tables (even if it did, this could not be relied on,
+/*     because the process may be terminated between table updates).
+/*     The read-write service should therefore not be used with tables that
+/*     leave persistent storage in an inconsistent state between
+/*     updates (for example, CDB). Tables that support "sync on
+/*     update" should be safe (for example, Berkeley DB) as should
+/*     tables that are implemented by a real DBMS.
 /* CONFIGURATION PARAMETERS
 /* .ad
 /* .fi
 /* .IP "\fBprocess_name (read-only)\fR"
 /*     The process name of a Postfix command or daemon process.
 /* .IP "\fBproxy_read_maps (see 'postconf -d' output)\fR"
-/*     The lookup tables that the \fBproxymap\fR(8) server is allowed to access.
+/*     The lookup tables that the \fBproxymap\fR(8) server is allowed to
+/*     access for the read-only service.
+/* .PP
+/*     Available in Postfix 2.5 and later:
+/* .IP "\fBproxy_write_maps (see 'postconf -d' output)\fR"
+/*     The lookup tables that the \fBproxymap\fR(8) server is allowed to
+/*     access for the read-write service.
 /* SEE ALSO
 /*     postconf(5), configuration parameters
 /*     master(5), generic daemon options
@@ -189,11 +225,12 @@ char   *var_rcpt_canon_maps;
 char   *var_relocated_maps;
 char   *var_transport_maps;
 char   *var_proxy_read_maps;
+char   *var_proxy_write_maps;
 
  /*
   * The pre-approved, pre-parsed list of maps.
   */
-static HTABLE *proxy_read_maps;
+static HTABLE *proxy_auth_maps;
 
  /*
   * Shared and static to reduce memory allocation overhead.
@@ -201,8 +238,14 @@ static HTABLE *proxy_read_maps;
 static VSTRING *request;
 static VSTRING *request_map;
 static VSTRING *request_key;
+static VSTRING *request_value;
 static VSTRING *map_type_name_flags;
 
+ /*
+  * Are we a proxy writer or not?
+  */
+static int proxy_writer;
+
  /*
   * Silly little macros.
   */
@@ -219,6 +262,7 @@ static DICT *proxy_map_find(const char *map_type_name, int request_flags,
 #define PROXY_COLON    DICT_TYPE_PROXY ":"
 #define PROXY_COLON_LEN        (sizeof(PROXY_COLON) - 1)
 #define READ_OPEN_FLAGS        O_RDONLY
+#define WRITE_OPEN_FLAGS (O_RDWR | O_CREAT)
 
     /*
      * Canonicalize the map name. If the map is not on the approved list,
@@ -230,11 +274,13 @@ static DICT *proxy_map_find(const char *map_type_name, int request_flags,
        map_type_name += PROXY_COLON_LEN;
     if (strchr(map_type_name, ':') == 0)
        PROXY_MAP_FIND_ERROR_RETURN(PROXY_STAT_BAD);
-    if (htable_locate(proxy_read_maps, map_type_name) == 0) {
+    if (htable_locate(proxy_auth_maps, map_type_name) == 0) {
        msg_warn("request for unapproved table: \"%s\"", map_type_name);
        msg_warn("to approve this table for %s access, list %s:%s in %s:%s",
-                MAIL_SERVICE_PROXYMAP, DICT_TYPE_PROXY, map_type_name,
-                MAIN_CONF_FILE, VAR_PROXY_READ_MAPS);
+                proxy_writer == 0 ? "read-only" : "read-write",
+                DICT_TYPE_PROXY, map_type_name, MAIN_CONF_FILE,
+                proxy_writer == 0 ? VAR_PROXY_READ_MAPS :
+                VAR_PROXY_WRITE_MAPS);
        PROXY_MAP_FIND_ERROR_RETURN(PROXY_STAT_DENY);
     }
 
@@ -243,13 +289,21 @@ static DICT *proxy_map_find(const char *map_type_name, int request_flags,
      * 
      * Assume that a map instance can be shared among clients with different
      * paranoia flag settings and with different map lookup flag settings.
+     * 
+     * XXX The open() flags are passed implicitly, via the selection of the
+     * service name. For a more sophisticated interface, appropriate subsets
+     * of open() flags should be received directly from the client.
      */
     vstring_sprintf(map_type_name_flags, "%s:%s", map_type_name,
                    dict_flags_str(request_flags & DICT_FLAG_NP_INST_MASK));
     if ((dict = dict_handle(STR(map_type_name_flags))) == 0)
-       dict = dict_open(map_type_name, READ_OPEN_FLAGS, request_flags);
+       dict = dict_open(map_type_name, proxy_writer ?
+                        WRITE_OPEN_FLAGS : READ_OPEN_FLAGS,
+                        request_flags);
     if (dict == 0)
        msg_panic("proxy_map_find: dict_open null result");
+    if (proxy_writer)
+       dict->flags |= DICT_FLAG_SYNC_UPDATE;
     dict_register(STR(map_type_name_flags), dict);
     return (dict);
 }
@@ -297,6 +351,46 @@ static void proxymap_lookup_service(VSTREAM *client_stream)
               ATTR_TYPE_END);
 }
 
+/* proxymap_update_service - remote update service */
+
+static void proxymap_update_service(VSTREAM *client_stream)
+{
+    int     request_flags;
+    DICT   *dict;
+    int     reply_status;
+
+    /*
+     * Process the request.
+     */
+    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
+                 ATTR_TYPE_STR, MAIL_ATTR_TABLE, request_map,
+                 ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &request_flags,
+                 ATTR_TYPE_STR, MAIL_ATTR_KEY, request_key,
+                 ATTR_TYPE_STR, MAIL_ATTR_VALUE, request_value,
+                 ATTR_TYPE_END) != 4) {
+       reply_status = PROXY_STAT_BAD;
+    } else if (proxy_writer == 0) {
+       msg_warn("refusing %s update request on non-%s service",
+                STR(request_map), MAIL_SERVICE_PROXYWRITE);
+       reply_status = PROXY_STAT_DENY;
+    } else if ((dict = proxy_map_find(STR(request_map), request_flags,
+                                     &reply_status)) == 0) {
+        /* void */ ;
+    } else {
+       dict->flags = ((dict->flags & ~DICT_FLAG_RQST_MASK)
+                      | (request_flags & DICT_FLAG_RQST_MASK));
+       dict_put(dict, STR(request_key), STR(request_value));
+       reply_status = PROXY_STAT_OK;
+    }
+
+    /*
+     * Respond to the client.
+     */
+    attr_print(client_stream, ATTR_FLAG_NONE,
+              ATTR_TYPE_INT, MAIL_ATTR_STATUS, reply_status,
+              ATTR_TYPE_END);
+}
+
 /* proxymap_open_service - open remote lookup table */
 
 static void proxymap_open_service(VSTREAM *client_stream)
@@ -355,6 +449,8 @@ static void proxymap_service(VSTREAM *client_stream, char *unused_service,
                  ATTR_TYPE_END) == 1) {
        if (VSTREQ(request, PROXY_REQ_LOOKUP)) {
            proxymap_lookup_service(client_stream);
+       } else if (VSTREQ(request, PROXY_REQ_UPDATE)) {
+           proxymap_update_service(client_stream);
        } else if (VSTREQ(request, PROXY_REQ_OPEN)) {
            proxymap_open_service(client_stream);
        } else {
@@ -381,26 +477,37 @@ DICT   *dict_proxy_open(const char *map, int open_flags, int dict_flags)
 
 /* post_jail_init - initialization after privilege drop */
 
-static void post_jail_init(char *unused_name, char **unused_argv)
+static void post_jail_init(char *service_name, char **unused_argv)
 {
     const char *sep = ", \t\r\n";
     char   *saved_filter;
     char   *bp;
     char   *type_name;
 
+    /*
+     * Are we proxy writer?
+     */
+    if (strcmp(service_name, MAIL_SERVICE_PROXYWRITE) == 0)
+       proxy_writer = 1;
+    else if (strcmp(service_name, MAIL_SERVICE_PROXYMAP) != 0)
+       msg_fatal("service name must be one of %s or %s",
+                 MAIL_SERVICE_PROXYMAP, MAIL_SERVICE_PROXYMAP);
+
     /*
      * Pre-allocate buffers.
      */
     request = vstring_alloc(10);
     request_map = vstring_alloc(10);
     request_key = vstring_alloc(10);
+    request_value = vstring_alloc(10);
     map_type_name_flags = vstring_alloc(10);
 
     /*
      * Prepare the pre-approved list of proxied tables.
      */
-    saved_filter = bp = mystrdup(var_proxy_read_maps);
-    proxy_read_maps = htable_create(13);
+    saved_filter = bp = mystrdup(proxy_writer ? var_proxy_write_maps :
+                                var_proxy_read_maps);
+    proxy_auth_maps = htable_create(13);
     while ((type_name = mystrtok(&bp, sep)) != 0) {
        if (strncmp(type_name, PROXY_COLON, PROXY_COLON_LEN))
            continue;
@@ -408,8 +515,8 @@ static void post_jail_init(char *unused_name, char **unused_argv)
            type_name += PROXY_COLON_LEN;
        } while (!strncmp(type_name, PROXY_COLON, PROXY_COLON_LEN));
        if (strchr(type_name, ':') != 0
-           && htable_locate(proxy_read_maps, type_name) == 0)
-           (void) htable_enter(proxy_read_maps, type_name, (char *) 0);
+           && htable_locate(proxy_auth_maps, type_name) == 0)
+           (void) htable_enter(proxy_auth_maps, type_name, (char *) 0);
     }
     myfree(saved_filter);
 
@@ -418,6 +525,13 @@ static void post_jail_init(char *unused_name, char **unused_argv)
      * time, so we don't have to do it another time.
      */
     var_idle_limit = 1;
+
+    /*
+     * Never, ever, get killed by a master signal, as that could corrupt a
+     * persistent database when we're in the middle of an update.
+     */
+    if (proxy_writer != 0)
+       setsid();
 }
 
 /* pre_accept - see if tables have changed */
@@ -426,7 +540,7 @@ static void pre_accept(char *unused_name, char **unused_argv)
 {
     const char *table;
 
-    if ((table = dict_changed_name()) != 0) {
+    if (proxy_writer == 0 && (table = dict_changed_name()) != 0) {
        msg_info("table %s has changed -- restarting", table);
        exit(0);
     }
@@ -452,6 +566,7 @@ int     main(int argc, char **argv)
        VAR_RELOCATED_MAPS, DEF_RELOCATED_MAPS, &var_relocated_maps, 0, 0,
        VAR_TRANSPORT_MAPS, DEF_TRANSPORT_MAPS, &var_transport_maps, 0, 0,
        VAR_PROXY_READ_MAPS, DEF_PROXY_READ_MAPS, &var_proxy_read_maps, 0, 0,
+       VAR_PROXY_WRITE_MAPS, DEF_PROXY_WRITE_MAPS, &var_proxy_write_maps, 0, 0,
        0,
     };
 
@@ -464,5 +579,6 @@ int     main(int argc, char **argv)
                      MAIL_SERVER_STR_TABLE, str_table,
                      MAIL_SERVER_POST_INIT, post_jail_init,
                      MAIL_SERVER_PRE_ACCEPT, pre_accept,
+    /* XXX MAIL_SERVER_SOLITARY if proxywrite */
                      0);
 }
index f96e853b4477230bf4d2fa84a00f10582b27569f..8d9a1528e64c1ed112ad1445f182070d11639405 100644 (file)
@@ -306,7 +306,7 @@ void    qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
        queue->fail_cohorts += 1.0 / queue->window;
        if (transport->fail_cohort_limit > 0
            && queue->fail_cohorts >= transport->fail_cohort_limit)
-           queue->window = 0;
+           queue->window = QMGR_QUEUE_STAT_THROTTLED;
     }
 
     /*
index de1691f08ce7e8835a2f51a6cf134b8d50ddd711..9829d28effb4ddb1a607bf4cea3f210761c3cf15 100644 (file)
@@ -95,7 +95,9 @@ extern DICT *dict_debug(DICT *);
 #define DICT_FLAG_PARANOID \
        (DICT_FLAG_NO_REGSUB | DICT_FLAG_NO_PROXY | DICT_FLAG_NO_UNAUTH)
 #define DICT_FLAG_IMPL_MASK    (DICT_FLAG_FIXED | DICT_FLAG_PATTERN)
-#define DICT_FLAG_RQST_MASK    DICT_FLAG_FOLD_ANY
+#define DICT_FLAG_RQST_MASK    (DICT_FLAG_FOLD_ANY | DICT_FLAG_LOCK | \
+                               DICT_FLAG_DUP_REPLACE | DICT_FLAG_DUP_WARN | \
+                               DICT_FLAG_SYNC_UPDATE)
 #define DICT_FLAG_NP_INST_MASK ~(DICT_FLAG_IMPL_MASK | DICT_FLAG_RQST_MASK)
 #define DICT_FLAG_INST_MASK    (DICT_FLAG_NP_INST_MASK | DICT_FLAG_PARANOID)