]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
snapshot-19991209
authorWietse Venema <wietse@porcupine.org>
Thu, 9 Dec 1999 05:00:00 +0000 (00:00 -0500)
committerWietse Venema <wietse@porcupine.org>
Thu, 17 Jan 2013 23:09:47 +0000 (18:09 -0500)
24 files changed:
postfix/DEBUG_README
postfix/HISTORY
postfix/RELEASE_NOTES
postfix/conf/main.cf
postfix/conf/sample-local.cf
postfix/conf/sample-smtpd.cf
postfix/global/been_here.c
postfix/global/been_here.h
postfix/global/mail_version.h
postfix/html/postalias.1.html
postfix/html/postmap.1.html
postfix/html/uce.html
postfix/local/dotforward.c
postfix/local/token.c
postfix/man/man1/postalias.1
postfix/man/man1/postmap.1
postfix/postalias/postalias.c
postfix/postconf/postconf.c
postfix/postmap/postmap.c
postfix/qmgr/qmgr_entry.c
postfix/qmgr/qmgr_message.c
postfix/util/dict_ldap.c
postfix/util/dict_mysql.c
postfix/util/dict_mysql.h

index 89ca9913621da2a027cc47fe5b5e8f5e35cfc81d..78d0b6dd5c71236d7e854196a02a3571a0372512 100644 (file)
@@ -25,8 +25,8 @@ Append one or more -v options to selected daemon definitions in
 /etc/postfix/master.cf and type "postfix reload". This will cause
 a lot of activity to be logged to the syslog daemon.
 
-4 - Tracing a Postfix daemon process
-====================================
+4 - Manually tracing a Postfix daemon process
+=============================================
 
 Some systems allow you to inspect a running process with a system
 call tracer. For example:
@@ -41,12 +41,30 @@ See your system documentation for details.
 Tracing a running process can give valuable information about what
 a process is attempting to do. This is as much information as you
 can get without running an interactive debugger program, as described
-in the next section.
+in a later section.
 
-See the next section on how to automatically attach a program to
-a Postfix daemon.
+5 - Automatically tracing a Postfix daemon process
+==================================================
 
-5 - Running daemon programs under an interactive debugger
+Postfix can attach a call tracer whenever a daemon process starts.
+
+Append a -D option to the suspect command in /etc/postfix/master.cf,
+for example:
+
+    smtp      inet  n       -       n       -       -       smtpd -D
+
+Edit the debugger_command definition in /etc/postfix/main.cf so
+that it invokes the call tracer of your choice, for example:
+
+    debugger_command =
+         PATH=/bin:/usr/bin:/usr/local/bin
+         (truss -p $process_id 2>&1 | logger -p mail.info) & sleep 5
+
+Instead of truss use trace or strace.
+
+Type "postfix reload" and watch the logfile.
+
+6 - Running daemon programs under an interactive debugger
 =========================================================
 
 Append a -D option to the suspect command in /etc/postfix/master.cf,
@@ -72,7 +90,7 @@ Stop and start the Postfix system.
 Whenever the suspect daemon process is started, a debugger window
 pops up and you can watch in detail what happens.
 
-6 - Unreasonable behavior
+7 - Unreasonable behavior
 =========================
 
 Sometimes the behavior exhibit by Postfix just does not match the
index d6b13adaa2d2664860dc447d6b952ba023a7a7d4..67a397a0ab5e79891ef81a951efa83381f6899b0 100644 (file)
@@ -3317,15 +3317,43 @@ Apologies for any names omitted.
 
 19991207
 
-       Performance: the queue manager now frees in-memory recipients
-       as soon as a message is delivered to one destination, rather
-       than waiting until all in-memory recipients of that message
-       have been tried. This means that one message with many
-       recipients no longer stops other mail from being delivered.
+       Performance: one message with many recipients no longer
+       stops other mail from being delivered. The queue manager
+       now frees in-memory recipients as soon as a message is
+       delivered to one destination, rather than waiting until
+       all in-memory destinations of that message have been tried.
        Patch by Patrik Rak @ ein.cz. Files: qmgr/qmgr_entry.c,
        qmgr/qmgr_message.c.
 
-       Performance: when delivering a huge list of recipients,
-       the queue manager now reads new recipients from queue file
-       before delivery concurrency starts dropping.  Files:
-       qmgr/qmgr_entry.c, qmgr/qmgr_message.c.
+       Performance: when delivering mail to a huge list of
+       recipients, the queue manager now reads more recipients
+       from the queue file before delivery concurrency starts
+       to drop.  Files:  qmgr/qmgr_entry.c, qmgr/qmgr_message.c.
+
+19991208
+
+       Performance: improved worst-case behavior. A fully loaded
+       Postfix inflicts the same delay to messages with any number
+       of recipients (up to qmgr_message_recipient_limit.) Inspired
+       by discussions with Patrik Rak (although he disagrees with
+       the strategy).  File: qmgr/qmgr_message.c.
+
+       Updated LDAP client code by John Hensley with escape
+       sequences as per RFC 2254. File: util/dict_ldap.c.
+
+       Updated MYSQL client code by Scott Cotton. File: dict_mysql.c.
+
+       Feature: added -N/-n options to include/exclude terminating
+       nulls in keys and values in postmap/postalias DB or DBM
+       files. Normally, Postfix uses whatever is appropriate for
+       the host system.  A non-default setting can be necessary
+       for inter-operability with third-party software.
+
+       Bugfix: the local delivery agent would deliver to the user
+       instead of the .forward file when the .forward file was
+       already visited via some non-recursive path. Patch by Patrik
+       Rak @ ein.cz. Files: global/been_here.c, local/dotforward.c.
+
+       Robustness: attempt to deliver all addresses in the expansion
+       of an alias or .forward file, even when some addresses must
+       be deferred.  File: local/token.c.
index e4449762c627f6109a58e03bab08705f9faabd46..4e5024b0ca67a434092a5c6f6ed36b020356f4cb 100644 (file)
@@ -1,4 +1,4 @@
-Incompatible changes with snapshot 19991127
+Incompatible changes with snapshot 19991209
 ===========================================
 
 - In an SMTPD access map, an all-numeric right-hand side now means
@@ -18,7 +18,7 @@ $mydestination domains matches a transport specification, you also
 need to add a "domain.name local:" entry in your transport_maps.
 See the html/faq.html sections for firewalls and intranets.
 
-Major changes with snapshot 19991127
+Major changes with snapshot 19991209
 ====================================
 
 - It is now relatively safe to configure 550 status codes for the
index 869b2bbcb93821cc6f2dedffab19738b164d30f4..6fbc69cb0c45d2942a31f0ff4e2fb1209ae52676 100644 (file)
@@ -222,9 +222,9 @@ mail_owner = postfix
 # DELIVERY TO MAILBOX
 #
 # The home_mailbox parameter specifies the optional pathname of a
-# mailbox relative to a user's home directory. The default is to
-# deliver to the UNIX-style /var/spool/mail/user or /var/mail/user.
-# Specify "Maildir/" for qmail-style delivery (the / is required).
+# mailbox file relative to a user's home directory. The default
+# mailbox file is /var/spool/mail/user or /var/mail/user.  Specify
+# "Maildir/" for qmail-style delivery (the / is required).
 #
 #home_mailbox = Mailbox
 #home_mailbox = Maildir/
@@ -298,12 +298,14 @@ mail_owner = postfix
 #header_checks = regexp:/etc/postfix/filename
 #header_checks = pcre:/etc/postfix/filename
 
-# The relay_domains parameter restricts what domains (and subdomains
-# thereof) this mail system will relay mail from or to.  See the
-# smtpd_recipient_restrictions restriction in the file sample-smtpd.cf.
+# The relay_domains parameter restricts what client hostname domains
+# (and subdomains thereof) this mail system will relay mail from,
+# and restricts what destination domains (and subdomains thereof)
+# this system will relay mail to.  See the smtpd_recipient_restrictions
+# restriction in the file sample-smtpd.cf.
 #
-# By default, Postfix relays mail only from or to sites in or below
-# $mydestination, or in the optional virtual domain list.
+# By default, Postfix relays mail only from clients or to destinations
+# in or below $mydestination, or in the optional virtual domain list.
 # 
 # Specify a list of hosts or domains, /file/name patterns or type:name
 # lookup tables, separated by commas and/or whitespace.  Continue
index 2cb6ff6d4a7f1577c1430aa56f851c8edbd47001..f2c3d4ebe4b0feb4625ae1cf4c8806f00d6fb9d9 100644 (file)
@@ -66,9 +66,9 @@ default_privs = nobody
 #
 
 # The home_mailbox parameter specifies the optional pathname of a
-# mailbox relative to a user's home directory. The default is to
-# deliver to the UNIX-style /var/spool/mail/user or /var/mail/user.
-# Specify "Maildir/" for qmail-style delivery (the / is required).
+# mailbox file relative to a user's home directory. The default
+# mailbox file is /var/spool/mail/user or /var/mail/user.  Specify
+# "Maildir/" for qmail-style delivery (the / is required).
 #
 # home_mailbox = Mailbox
 # home_mailbox = Maildir/
index db6f35f692b864844a1d0f25d75726a1eb448181..9c37a82c3981cf22cd8e9bf7b7dabbf0704faf6a 100644 (file)
@@ -181,7 +181,7 @@ smtpd_sender_restrictions =
 # 
 # The default is to permit any destination from clients that match
 # $mynetworks, and to otherwise permit only mail from clients or to
-# domains that match $relay_domains or a subdomain thereof.
+# destinations that match $relay_domains or a subdomain thereof.
 #
 # The following restrictions are available:
 #
@@ -191,8 +191,8 @@ smtpd_sender_restrictions =
 #   reject_invalid_hostname: reject HELO hostname with bad syntax.
 #   reject_unknown_hostname: reject HELO hostname without DNS A or MX record.
 #   reject_unknown_sender_domain: reject sender domain without A or MX record.
-#   check_relay_domains: permit only mail from/to domains in $relay_domains
-       or to the local machine.
+#   check_relay_domains: permit only mail from clients/to domains matching 
+#      $relay_domains, or to the local machine.
 #   permit_auth_destination: permit mail to self or to $relay_domains.
 #   reject_unauth_destination: reject mail not to self or to $relay_domains.
 #   reject_unauth_pipelining: reject mail from improperly pipelining spamware
@@ -238,11 +238,13 @@ smtpd_recipient_restrictions = permit_mynetworks,check_relay_domains
 #
 maps_rbl_domains = rbl.maps.vix.com
 
-# The relay_domains parameter restricts what domains (and subdomains
-# thereof) this mail system will relay mail from or to.
+# The relay_domains parameter restricts what client hostname domains
+# (and subdomains thereof) this mail system will relay mail from,
+# and restricts what destination domains (and subdomains thereof)
+# this system will relay mail to.
 # 
-# By default, Postfix relays mail only from or to sites in or below
-# $mydestination, or in the optional virtual domain list.
+# By default, Postfix relays mail only from clients or to destinations
+# in or below $mydestination, or in the optional virtual domain list.
 # 
 # Specify a list of hosts or domains, /file/name patterns or type:name
 # lookup tables, separated by commas and/or whitespace.  Continue
index 51e8e871581246cf74d73bdd46222eae63170f52..a95c91acac89fc4fff57d1fab81c291a63eca1d9 100644 (file)
 /*     BH_TABLE *dup_filter;
 /*     char    *format;
 /*
+/*     int     been_here_check_fixed(dup_filter, string)
+/*     BH_TABLE *dup_filter;
+/*     char    *string;
+/*
+/*     int     been_here_check(dup_filter, format, ...)
+/*     BH_TABLE *dup_filter;
+/*     char    *format;
+/*
 /*     void    been_here_free(dup_filter)
 /*     BH_TABLE *dup_filter;
 /* DESCRIPTION
@@ -34,6 +42,9 @@
 /*     not found. The result is non-zero (true) if the formatted result was
 /*     found, zero (false) otherwise.
 /*
+/*     been_here_check_fixed() and been_here_check() are similar
+/*     but do not update the duplicate filter.
+/*
 /*     been_here_free() releases storage for a duplicate filter.
 /*
 /*     Arguments:
@@ -173,3 +184,65 @@ int     been_here_fixed(BH_TABLE *dup_filter, const char *string)
 
     return (status);
 }
+
+/* been_here_check - query duplicate detector with finer control */
+
+int     been_here_check(BH_TABLE *dup_filter, const char *fmt,...)
+{
+    VSTRING *buf = vstring_alloc(100);
+    int     status;
+    va_list ap;
+
+    /*
+     * Construct the string to be checked.
+     */
+    va_start(ap, fmt);
+    vstring_vsprintf(buf, fmt, ap);
+    va_end(ap);
+
+    /*
+     * Do the duplicate check.
+     */
+    status = been_here_check_fixed(dup_filter, vstring_str(buf));
+
+    /*
+     * Cleanup.
+     */
+    vstring_free(buf);
+    return (status);
+}
+
+/* been_here_check_fixed - query duplicate detector */
+
+int     been_here_check_fixed(BH_TABLE *dup_filter, const char *string)
+{
+    char   *folded_string;
+    const char *lookup_key;
+    int     status;
+
+    /*
+     * Special processing: case insensitive lookup.
+     */
+    if (dup_filter->flags & BH_FLAG_FOLD) {
+       folded_string = mystrdup(string);
+       lookup_key = lowercase(folded_string);
+    } else {
+       folded_string = 0;
+       lookup_key = string;
+    }
+
+    /*
+     * Do the duplicate check.
+     */
+    status = (htable_locate(dup_filter->table, lookup_key) != 0);
+    if (msg_verbose)
+       msg_info("been_here_check: %s: %d", string, status);
+
+    /*
+     * Cleanup.
+     */
+    if (folded_string)
+       myfree(folded_string);
+
+    return (status);
+}
index 953889784999dd3b35167007943c9b357ca73eba..12d87848741b19677f6e3932e2b1d4a1323e566c 100644 (file)
@@ -32,6 +32,8 @@ extern BH_TABLE *been_here_init(int, int);
 extern void been_here_free(BH_TABLE *);
 extern int been_here_fixed(BH_TABLE *, const char *);
 extern int been_here(BH_TABLE *, const char *,...);
+extern int been_here_check_fixed(BH_TABLE *, const char *);
+extern int been_here_check(BH_TABLE *, const char *,...);
 
 /* LICENSE
 /* .ad
index 5c873b6450931651285400d319c1ee8fceac1e1c..f963aed29b89d7df0a50480b42cd1d94a153fc5e 100644 (file)
@@ -15,7 +15,7 @@
   * Version of this program.
   */
 #define VAR_MAIL_VERSION       "mail_version"
-#define DEF_MAIL_VERSION       "Snapshot-19991207"
+#define DEF_MAIL_VERSION       "Snapshot-19991209"
 extern char *var_mail_version;
 
 /* LICENSE
index 4e25fad1f3a3c1b9e72f4c20525efc8843582525..4d95379f7e0275c1be3ed5a4a1b01ad8505248a9 100644 (file)
@@ -9,7 +9,7 @@ POSTALIAS(1)                                         POSTALIAS(1)
        postalias - Postfix alias database maintenance
 
 <b>SYNOPSIS</b>
-       <b>postalias</b> [<b>-ivw</b>] [<b>-c</b> <i>config_dir</i>] [<b>-q</b> <i>key</i>]
+       <b>postalias</b> [<b>-Ninvw</b>] [<b>-c</b> <i>config_dir</i>] [<b>-q</b> <i>key</i>]
        [<i>file_type</i>:]<i>file_name</i> ...
 
 <b>DESCRIPTION</b>
@@ -26,15 +26,25 @@ POSTALIAS(1)                                         POSTALIAS(1)
 
        Options:
 
+       <b>-N</b>     Include  the terminating null character that termi-
+              nates lookup keys and values. By  default,  Postfix
+              does whatever is the default for the host operating
+              system.
+
        <b>-c</b> <i>config_dir</i>
-              Read  the  <b>main.cf</b>  configuration file in the named
+              Read the <b>main.cf</b> configuration file  in  the  named
               directory.
 
-       <b>-i</b>     Incremental mode. Read entries from standard  input
+       <b>-i</b>     Incremental  mode. Read entries from standard input
               and  do  not  truncate  an  existing  database.  By
-              default, <b>postalias</b> creates a new database from  the
+              default,  <b>postalias</b> creates a new database from the
               entries in <b>file</b><i>_</i><b>name</b>.
 
+       <b>-n</b>     Don't include the terminating null  character  that
+              terminates  lookup  keys  and  values.  By default,
+              Postfix does whatever is the default for  the  host
+              operating system.
+
        <b>-q</b> <i>key</i> Search  the  specified  maps  for <i>key</i> and print the
               first value found on the  standard  output  stream.
               The exit status is non-zero if the requested infor-
@@ -49,16 +59,6 @@ POSTALIAS(1)                                         POSTALIAS(1)
 
        Arguments:
 
-       <i>file_type</i>
-              The type of database to be produced.
-
-              <b>btree</b>  The   output   is   a   btree   file,  named
-                     <i>file_name</i><b>.db</b>.  This  is  available  only  on
-                     systems with support for <b>db</b> databases.
-
-              <b>dbm</b>    The  output  consists  of  two  files, named
-                     <i>file_name</i><b>.pag</b> and  <i>file_name</i><b>.dir</b>.   This  is
-                     available  only  on systems with support for
 
 
 
@@ -71,6 +71,16 @@ POSTALIAS(1)                                         POSTALIAS(1)
 POSTALIAS(1)                                         POSTALIAS(1)
 
 
+       <i>file_type</i>
+              The type of database to be produced.
+
+              <b>btree</b>  The   output   is   a   btree   file,  named
+                     <i>file_name</i><b>.db</b>.  This  is  available  only  on
+                     systems with support for <b>db</b> databases.
+
+              <b>dbm</b>    The  output  consists  of  two  files, named
+                     <i>file_name</i><b>.pag</b> and  <i>file_name</i><b>.dir</b>.   This  is
+                     available  only  on systems with support for
                      <b>dbm</b> databases.
 
               <b>hash</b>   The  output  is   a   hashed   file,   named
@@ -114,6 +124,19 @@ POSTALIAS(1)                                         POSTALIAS(1)
        <a href="aliases.5.html">aliases(5)</a> format of alias database input file.
        <a href="sendmail.1.html">sendmail(1)</a> mail posting and compatibility interface.
 
+
+
+
+
+                                                                2
+
+
+
+
+
+POSTALIAS(1)                                         POSTALIAS(1)
+
+
 <b>LICENSE</b>
        The  Secure  Mailer  license must be distributed with this
        software.
@@ -128,7 +151,50 @@ POSTALIAS(1)                                         POSTALIAS(1)
 
 
 
-                                                                2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+                                                                3
 
 
 </pre> </body> </html>
index 263a52fbdcf394e03840b0eb1c4e5b61e6ee2e0a..c818e43e0f781881776b941f8122ee8e138ae08e 100644 (file)
@@ -9,7 +9,7 @@ POSTMAP(1)                                             POSTMAP(1)
        postmap - Postfix lookup table management
 
 <b>SYNOPSIS</b>
-       <b>postmap</b> [<b>-ivw</b>] [<b>-c</b> <i>config_dir</i>] [<b>-q</b> <i>key</i>]
+       <b>postmap</b> [<b>-Ninvw</b>] [<b>-c</b> <i>config_dir</i>] [<b>-q</b> <i>key</i>]
        [<i>file_type</i>:]<i>file_name</i> ...
 
 <b>DESCRIPTION</b>
@@ -45,20 +45,20 @@ POSTMAP(1)                                             POSTMAP(1)
 
        Options:
 
+       <b>-N</b>     Include the terminating null character that  termi-
+              nates  lookup  keys and values. By default, Postfix
+              does whatever is the default for the host operating
+              system.
+
        <b>-c</b> <i>config_dir</i>
-              Read the <b>main.cf</b> configuration file  in  the  named
+              Read  the  <b>main.cf</b>  configuration file in the named
               directory.
 
-       <b>-i</b>     Incremental  mode. Read entries from standard input
+       <b>-i</b>     Incremental mode. Read entries from standard  input
               and  do  not  truncate  an  existing  database.  By
-              default,  <b>postmap</b>  creates  a new database from the
+              default, <b>postmap</b> creates a new  database  from  the
               entries in <b>file</b><i>_</i><b>name</b>.
 
-       <b>-q</b> <i>key</i> Search the specified maps for  <i>key</i>  and  print  the
-              first  value  found  on the standard output stream.
-              The exit status is non-zero if the requested infor-
-              mation was not found.
-
 
 
 
@@ -71,6 +71,16 @@ POSTMAP(1)                                             POSTMAP(1)
 POSTMAP(1)                                             POSTMAP(1)
 
 
+       <b>-n</b>     Don't  include  the terminating null character that
+              terminates lookup  keys  and  values.  By  default,
+              Postfix  does  whatever is the default for the host
+              operating system.
+
+       <b>-q</b> <i>key</i> Search the specified maps for  <i>key</i>  and  print  the
+              first  value  found  on the standard output stream.
+              The exit status is non-zero if the requested infor-
+              mation was not found.
+
        <b>-v</b>     Enable verbose logging for debugging purposes. Mul-
               tiple <b>-v</b> options  make  the  software  increasingly
               verbose.
@@ -116,16 +126,6 @@ POSTMAP(1)                                             POSTMAP(1)
        <b>MAIL</b><i>_</i><b>VERBOSE</b>
               Enable verbose logging for debugging purposes.
 
-<b>CONFIGURATION</b> <b>PARAMETERS</b>
-       <b>database</b><i>_</i><b>type</b>
-              Default output database type.  On  many  UNIX  sys-
-              tems,  the  default database type is either <b>hash</b> or
-              <b>dbm</b>.
-
-<b>LICENSE</b>
-       The Secure Mailer license must be  distributed  with  this
-       software.
-
 
 
                                                                 2
@@ -137,6 +137,16 @@ POSTMAP(1)                                             POSTMAP(1)
 POSTMAP(1)                                             POSTMAP(1)
 
 
+<b>CONFIGURATION</b> <b>PARAMETERS</b>
+       <b>database</b><i>_</i><b>type</b>
+              Default output database type.  On  many  UNIX  sys-
+              tems,  the  default database type is either <b>hash</b> or
+              <b>dbm</b>.
+
+<b>LICENSE</b>
+       The Secure Mailer license must be  distributed  with  this
+       software.
+
 <b>AUTHOR(S)</b>
        Wietse Venema
        IBM T.J. Watson Research
@@ -173,16 +183,6 @@ POSTMAP(1)                                             POSTMAP(1)
 
 
 
-
-
-
-
-
-
-
-
-
-
 
 
 
index 58db0ae5fc8af245ebfba6b1cab6ca7a415bebf3..9b518b2a43f9f1039bc26821d912074e491a673e 100644 (file)
@@ -550,7 +550,7 @@ specifies the response code for rejected requests (default:
 
 <dt> <b>permit_auth_destination</b> <dd> Ignore the client hostname.
 Permit the request when the resolved destination address matches
-the <a href="basic.html#mydestination">$mydestination</a>, the
+<a href="basic.html#mydestination">$mydestination</a>, the
 machine IP addresses, or <a href="#relay_domains"> $relay_domains</a>.
 
 <p>
index b65877f8664b271c120db32cc26533568a65aa4b..e3131d3f800e3e3b9b6e85c4595dba13da9cb202 100644 (file)
@@ -213,27 +213,31 @@ int     deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
      * If this user includes (an alias of) herself in her own .forward file,
      * deliver to the user instead.
      */
-    if (lookup_status >= 0
-       && been_here(state.dup_filter, "forward %s", STR(path)) == 0) {
-       state.msg_attr.exp_from = state.msg_attr.local;
-       if (S_ISREG(st.st_mode) == 0) {
-           msg_warn("file %s is not a regular file", STR(path));
-       } else if (st.st_uid != 0 && st.st_uid != usr_attr.uid) {
-           msg_warn("file %s has bad owner uid %d", STR(path), st.st_uid);
-       } else if (st.st_mode & 002) {
-           msg_warn("file %s is world writable", STR(path));
-       } else if ((fd = open_as(STR(path), O_RDONLY, 0, usr_attr.uid, usr_attr.gid)) < 0) {
-           msg_warn("cannot open file %s: %m", STR(path));
-       } else {
-           close_on_exec(fd, CLOSE_ON_EXEC);
-           addr_count = 0;
-           fp = vstream_fdopen(fd, O_RDONLY);
-           status = deliver_token_stream(state, usr_attr, fp, &addr_count);
-           if (vstream_fclose(fp))
-               msg_warn("close file %s: %m", STR(path));
-           if (addr_count > 0)
-               forward_found = YES;
-       }
+    if (lookup_status >= 0) {
+       if (been_here(state.dup_filter, "forward %s", STR(path)) == 0) {
+           state.msg_attr.exp_from = state.msg_attr.local;
+           if (S_ISREG(st.st_mode) == 0) {
+               msg_warn("file %s is not a regular file", STR(path));
+           } else if (st.st_uid != 0 && st.st_uid != usr_attr.uid) {
+               msg_warn("file %s has bad owner uid %d", STR(path), st.st_uid);
+           } else if (st.st_mode & 002) {
+               msg_warn("file %s is world writable", STR(path));
+           } else if ((fd = open_as(STR(path), O_RDONLY, 0, usr_attr.uid, usr_attr.gid)) < 0) {
+               msg_warn("cannot open file %s: %m", STR(path));
+           } else {
+               close_on_exec(fd, CLOSE_ON_EXEC);
+               addr_count = 0;
+               fp = vstream_fdopen(fd, O_RDONLY);
+               status = deliver_token_stream(state, usr_attr, fp, &addr_count);
+               if (vstream_fclose(fp))
+                   msg_warn("close file %s: %m", STR(path));
+               if (addr_count > 0) {
+                   forward_found = YES;
+                   been_here(state.dup_filter, "forward-done %s", STR(path));
+               }
+           }
+       } else if (been_here_check(state.dup_filter, "forward-done %s", STR(path)) != 0)
+           forward_found = YES;                /* else we're recursive */
     }
 
     /*
index d9ca86d4ca76577824040d6ef48f2db955922e47..9f6f8ac1452b61756b7d28ec26292991ce7e4167 100644 (file)
@@ -177,9 +177,7 @@ int     deliver_token_string(LOCAL_STATE state, USER_ATTR usr_attr,
        if (addr->type == TOK822_ADDR) {
            if (addr_count)
                (*addr_count)++;
-           status = deliver_token(state, usr_attr, addr);
-           if (status != 0)
-               break;
+           status |= deliver_token(state, usr_attr, addr);
        }
     }
     tok822_free_tree(tree);
index 5315b5d225e98edd6b6c17b7863bec9f41e72cbc..8c3880a4afcec9d3c7f9e92a3b6d5f09fae0e189 100644 (file)
@@ -9,8 +9,8 @@ Postfix alias database maintenance
 .na
 .nf
 .fi
-\fBpostalias\fR [\fB-ivw\fR] [\fB-c \fIconfig_dir\fR] [\fB-q \fIkey\fR]
-[\fIfile_type\fR:]\fIfile_name\fR ...
+\fBpostalias\fR [\fB-Ninvw\fR] [\fB-c \fIconfig_dir\fR]
+[\fB-q \fIkey\fR] [\fIfile_type\fR:]\fIfile_name\fR ...
 .SH DESCRIPTION
 .ad
 .fi
@@ -25,12 +25,20 @@ entire database, in order to avoid surprises in spectator
 programs.
 
 Options:
+.IP \fB-N\fR
+Include the terminating null character that terminates lookup keys
+and values. By default, Postfix does whatever is the default for
+the host operating system.
 .IP "\fB-c \fIconfig_dir\fR"
 Read the \fBmain.cf\fR configuration file in the named directory.
 .IP \fB-i\fR
 Incremental mode. Read entries from standard input and do not
 truncate an existing database. By default, \fBpostalias\fR creates
 a new database from the entries in \fBfile_name\fR.
+.IP \fB-n\fR
+Don't include the terminating null character that terminates lookup
+keys and values. By default, Postfix does whatever is the default for
+the host operating system.
 .IP "\fB-q \fIkey\fR"
 Search the specified maps for \fIkey\fR and print the first value
 found on the standard output stream. The exit status is non-zero
index f23c330d1ebdf03113e3c0396ee79aa94e5a76e8..0b8b593eee592a1f2dee38bb1b7c9b650466bff7 100644 (file)
@@ -9,7 +9,7 @@ Postfix lookup table management
 .na
 .nf
 .fi
-\fBpostmap\fR [\fB-ivw\fR] [\fB-c \fIconfig_dir\fR] [\fB-q \fIkey\fR]
+\fBpostmap\fR [\fB-Ninvw\fR] [\fB-c \fIconfig_dir\fR] [\fB-q \fIkey\fR]
 [\fIfile_type\fR:]\fIfile_name\fR ...
 .SH DESCRIPTION
 .ad
@@ -44,12 +44,20 @@ special characters such as `#' or whitespace. The \fIkey\fR is mapped
 to lowercase to make mapping lookups case insensitive.
 
 Options:
+.IP \fB-N\fR
+Include the terminating null character that terminates lookup keys
+and values. By default, Postfix does whatever is the default for
+the host operating system.
 .IP "\fB-c \fIconfig_dir\fR"
 Read the \fBmain.cf\fR configuration file in the named directory.
 .IP \fB-i\fR
 Incremental mode. Read entries from standard input and do not
 truncate an existing database. By default, \fBpostmap\fR creates
 a new database from the entries in \fBfile_name\fR.
+.IP \fB-n\fR
+Don't include the terminating null character that terminates lookup
+keys and values. By default, Postfix does whatever is the default for
+the host operating system.
 .IP "\fB-q \fIkey\fR"
 Search the specified maps for \fIkey\fR and print the first value
 found on the standard output stream. The exit status is non-zero
index 79f7f40ea78e685ed7744244653c9974fafbe678..2b002e88a08d276eb56149d0e8b1def1020a50ec 100644 (file)
@@ -5,8 +5,8 @@
 /*     Postfix alias database maintenance
 /* SYNOPSIS
 /* .fi
-/*     \fBpostalias\fR [\fB-ivw\fR] [\fB-c \fIconfig_dir\fR] [\fB-q \fIkey\fR]
-/*             [\fIfile_type\fR:]\fIfile_name\fR ...
+/*     \fBpostalias\fR [\fB-Ninvw\fR] [\fB-c \fIconfig_dir\fR]
+/*             [\fB-q \fIkey\fR] [\fIfile_type\fR:]\fIfile_name\fR ...
 /* DESCRIPTION
 /*     The \fBpostalias\fR command creates or queries one or more Postfix
 /*     alias databases, or updates an existing one. The input and output
 /*     programs.
 /*
 /*     Options:
+/* .IP \fB-N\fR
+/*     Include the terminating null character that terminates lookup keys
+/*     and values. By default, Postfix does whatever is the default for
+/*     the host operating system.
 /* .IP "\fB-c \fIconfig_dir\fR"
 /*     Read the \fBmain.cf\fR configuration file in the named directory.
 /* .IP \fB-i\fR
 /*     Incremental mode. Read entries from standard input and do not
 /*     truncate an existing database. By default, \fBpostalias\fR creates
 /*     a new database from the entries in \fBfile_name\fR.
+/* .IP \fB-n\fR
+/*     Don't include the terminating null character that terminates lookup
+/*     keys and values. By default, Postfix does whatever is the default for
+/*     the host operating system.
 /* .IP "\fB-q \fIkey\fR"
 /*     Search the specified maps for \fIkey\fR and print the first value
 /*     found on the standard output stream. The exit status is non-zero
@@ -297,7 +305,7 @@ static int postalias_query(const char *map_type, const char *map_name,
 
 static NORETURN usage(char *myname)
 {
-    msg_fatal("usage: %s [-ivw] [-c config_dir] [-q key] [map_type:]file...",
+    msg_fatal("usage: %s [-Ninvw] [-c config_dir] [-q key] [map_type:]file...",
              myname);
 }
 
@@ -346,11 +354,15 @@ int     main(int argc, char **argv)
     /*
      * Parse JCL.
      */
-    while ((ch = GETOPT(argc, argv, "c:iq:vw")) > 0) {
+    while ((ch = GETOPT(argc, argv, "Nc:inq:vw")) > 0) {
        switch (ch) {
        default:
            usage(argv[0]);
            break;
+       case 'N':
+           dict_flags |= DICT_FLAG_TRY1NULL;
+           dict_flags &= ~DICT_FLAG_TRY0NULL;
+           break;
        case 'c':
            if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
                msg_fatal("out of memory");
@@ -358,6 +370,10 @@ int     main(int argc, char **argv)
        case 'i':
            open_flags &= ~O_TRUNC;
            break;
+       case 'n':
+           dict_flags |= DICT_FLAG_TRY0NULL;
+           dict_flags &= ~DICT_FLAG_TRY1NULL;
+           break;
        case 'q':
            query = optarg;
            break;
index 4c5f2fbc00a1f93e6a863a3e8779f3e872eaf03d..f4695a44df16d0df9d090a33b8e7ec220078abc7 100644 (file)
@@ -638,6 +638,9 @@ int     main(int argc, char **argv)
     struct stat st;
     int     junk;
 
+    /*
+     * Be consistent with file permissions.
+     */
     umask(022);
 
     /*
index 702119aee360e7e427f703751d2db675fdd54ff3..d42909c13fdc7254d24e120fd68acf6b5a0d5917 100644 (file)
@@ -5,7 +5,7 @@
 /*     Postfix lookup table management
 /* SYNOPSIS
 /* .fi
-/*     \fBpostmap\fR [\fB-ivw\fR] [\fB-c \fIconfig_dir\fR] [\fB-q \fIkey\fR]
+/*     \fBpostmap\fR [\fB-Ninvw\fR] [\fB-c \fIconfig_dir\fR] [\fB-q \fIkey\fR]
 /*             [\fIfile_type\fR:]\fIfile_name\fR ...
 /* DESCRIPTION
 /*     The \fBpostmap\fR command creates or queries one or more Postfix
 /*     to lowercase to make mapping lookups case insensitive.
 /*
 /*     Options:
+/* .IP \fB-N\fR
+/*     Include the terminating null character that terminates lookup keys
+/*     and values. By default, Postfix does whatever is the default for
+/*     the host operating system.
 /* .IP "\fB-c \fIconfig_dir\fR"
 /*     Read the \fBmain.cf\fR configuration file in the named directory.
 /* .IP \fB-i\fR
 /*     Incremental mode. Read entries from standard input and do not
 /*     truncate an existing database. By default, \fBpostmap\fR creates
 /*     a new database from the entries in \fBfile_name\fR.
+/* .IP \fB-n\fR
+/*     Don't include the terminating null character that terminates lookup
+/*     keys and values. By default, Postfix does whatever is the default for
+/*     the host operating system.
 /* .IP "\fB-q \fIkey\fR"
 /*     Search the specified maps for \fIkey\fR and print the first value
 /*     found on the standard output stream. The exit status is non-zero
@@ -251,7 +259,7 @@ static int postmap_query(const char *map_type, const char *map_name,
 
 static NORETURN usage(char *myname)
 {
-    msg_fatal("usage: %s [-ivw] [-c config_dir] [-q key] [map_type:]file...",
+    msg_fatal("usage: %s [-Ninvw] [-c config_dir] [-q key] [map_type:]file...",
              myname);
 }
 
@@ -300,11 +308,15 @@ int     main(int argc, char **argv)
     /*
      * Parse JCL.
      */
-    while ((ch = GETOPT(argc, argv, "c:iq:vw")) > 0) {
+    while ((ch = GETOPT(argc, argv, "Nc:inq:vw")) > 0) {
        switch (ch) {
        default:
            usage(argv[0]);
            break;
+       case 'N':
+           dict_flags |= DICT_FLAG_TRY1NULL;
+           dict_flags &= ~DICT_FLAG_TRY0NULL;
+           break;
        case 'c':
            if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
                msg_fatal("out of memory");
@@ -312,6 +324,10 @@ int     main(int argc, char **argv)
        case 'i':
            open_flags &= ~O_TRUNC;
            break;
+       case 'n':
+           dict_flags |= DICT_FLAG_TRY0NULL;
+           dict_flags &= ~DICT_FLAG_TRY1NULL;
+           break;
        case 'q':
            query = optarg;
            break;
index f471e51fd709c19346db8f1c648b55a86f3dab9f..1d290a0ea179ab905cd9b7a3bf807e2b0d5be22a 100644 (file)
@@ -137,7 +137,7 @@ void    qmgr_entry_done(QMGR_ENTRY *entry, int which)
     }
 
     /*
-     * Free the recipient list and decrease in-core recipient count
+     * Free the recipient list and decrease the in-core recipient count
      * accordingly.
      */
     qmgr_recipient_count -= entry->rcpt_list.len;
@@ -160,10 +160,11 @@ void    qmgr_entry_done(QMGR_ENTRY *entry, int which)
 
     /*
      * Update the in-core message reference count. When the in-core message
-     * structure has no more references, dispose of the message. When the
-     * in-core recipient count falls below some threshold and this message
-     * has more recipients, read them from disk before concurrency starts to
-     * drop.
+     * structure has no more references, dispose of the message.
+     * 
+     * When the in-core recipient count falls below some threshold and this
+     * message has more recipients, read more recipients before concurrency
+     * starts to drop.
      */
     message->refcount--;
     if (message->refcount == 0)
index 0703913f90cfec4d3bd9852b9684128917f1a347..a3363ee6905cd1ac92a856567960bdcc49421e4a 100644 (file)
@@ -222,6 +222,17 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
      * may appear before or after the message content, so we keep reading
      * from the queue file until we have enough recipients (rcpt_offset != 0)
      * and until we know where the message content starts (data_offset != 0).
+     * 
+     * When reading recipients from queue file, stop reading when we reach a
+     * per-message in-core recipient limit rather than a global in-core
+     * recipient limit. Use the global recipient limit only in order to stop
+     * opening queue files. The purpose is to achieve equal delay for
+     * messages with recipient counts up to var_qmgr_rcpt_limit recipients.
+     * 
+     * If we would read recipients up to a global recipient limit, the average
+     * number of in-core recipients per message would asymptotically approach
+     * (global recipient limit)/(active queue size limit), which gives equal
+     * delay per recipient rather than equal delay per message.
      */
     do {
        if ((curr_offset = vstream_ftell(message->fp)) < 0)
@@ -241,10 +252,9 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
                       message->data_size, "queue %s", message->queue_name);
            }
        } else if (rec_type == REC_TYPE_RCPT) {
-#define TOTAL_RECIPIENT_COUNT (qmgr_recipient_count + message->rcpt_list.len)
-           if (TOTAL_RECIPIENT_COUNT < var_qmgr_rcpt_limit) {
+           if (message->rcpt_list.len < var_qmgr_rcpt_limit) {
                qmgr_rcpt_list_add(&message->rcpt_list, curr_offset, start);
-               if (TOTAL_RECIPIENT_COUNT >= var_qmgr_rcpt_limit) {
+               if (message->rcpt_list.len >= var_qmgr_rcpt_limit) {
                    if ((message->rcpt_offset = vstream_ftell(message->fp)) < 0)
                        msg_fatal("vstream_ftell %s: %m",
                                  VSTREAM_PATH(message->fp));
index e28725ae63a9f4e266da552fdcddd955e6778e42..f644f0e18c722496c1574f8c019c40962ffeeca6 100644 (file)
@@ -224,20 +224,39 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
     escaped_name = vstring_alloc(20);
     filter_buf = vstring_alloc(30);
 
-    /* Any wildcards and escapes in the supplied address should be escaped. */
-    if (strchr(name, '*') || strchr(name, '\\')) {
+    /*
+     * If any characters in the supplied address should be escaped per RFC
+     * 2254, do so.
+     */
+
+    end = (char *) name + strlen((char *) name);
+    sub = (char *) strpbrk((char *) name, "*()\\\0");
+    if (sub && sub != end) {
        if (msg_verbose)
-           msg_info("%s: found wildcard in %s", myname, name);
-       for (sub = (char *) name; *sub != '\0'; sub++) {
-           if (*sub == '*' || *sub == '\\') {
-               vstring_strncat(escaped_name, "\\", 1);
-               vstring_strncat(escaped_name, sub, 1);
-           } else {
+           msg_info("%s: found character(s) in %s that must be escaped", myname, name);
+       for (sub = (char *) name; sub != end; sub++) {
+           switch (*sub) {
+           case '*':
+               vstring_strcat(escaped_name, "\\2a");
+               break;
+           case '(':
+               vstring_strcat(escaped_name, "\\28");
+               break;
+           case ')':
+               vstring_strcat(escaped_name, "\\29");
+               break;
+           case '\\':
+               vstring_strcat(escaped_name, "\\5c");
+               break;
+           case '\0':
+               vstring_strcat(escaped_name, "\\00");
+               break;
+           default:
                vstring_strncat(escaped_name, sub, 1);
            }
        }
        if (msg_verbose)
-           msg_info("%s: with wildcards escaped, it's %s", myname, vstring_str(escaped_name));
+           msg_info("%s: after escaping, it's %s", myname, vstring_str(escaped_name));
     } else
        vstring_strcpy(escaped_name, (char *) name);
 
index 0da7e4880d7576c2c4159104f5172db585dcb016..8f3263779f789c99d7c25a62bab882c87f3a2e3f 100644 (file)
 /*     int     dummy;
 /*     int     unused_dict_flags;
 /* DESCRIPTION
-/*     dict_mysql_open() opens the mysql databases with name dbname on
-/*     each host in hostlist and registers under the given name with the
-/*     dictionary manager. The result is a pointer to the installed dictionary,
+/*     dict_mysql_open() creates a dictionary of type 'mysql'.  This
+/*     dictionary is an interface for the postfix key->value mappings
+/*     to mysql.  The result is a pointer to the installed dictionary,
 /*     or a null pointer in case of problems.
 /*
+/*     The mysql dictionary can manage multiple connections to different
+/*     sql servers on different hosts.  It assumes that the underlying data
+/*     on each host is identical (mirrored) and maintains one connection
+/*     at any given time.  If any connection fails,  any other available
+/*     ones will be opened and used.  The intent of this feature is to eliminate
+/*     a single point of failure for mail systems that would otherwise rely
+/*     on a single mysql server.
+/*
 /*     Arguments:
 /* .IP name
 /*     The path of the MySQL configuration file.  The file encodes a number of
 #include "argv.h"
 #include "vstring.h"
 
+/* external declarations */
 extern int dict_errno;
 
+/* need some structs to help organize things */
+typedef struct {
+    MYSQL   db;
+    char   *hostname;
+    int     stat;                      /* STATUNTRIED | STATFAIL | STATCUR */
+    time_t  ts;                                /* used for attempting reconnection
+                                        * every so often if a host is down */
+} HOST;
+
+typedef struct {
+    int     len_hosts;                 /* number of hosts */
+    HOST   *db_hosts;                  /* the hosts on which the databases
+                                        * reside */
+} PLMYSQL;
+
 typedef struct {
     char   *username;
     char   *password;
@@ -90,8 +114,248 @@ typedef struct {
     MYSQL_NAME *name;
 } DICT_MYSQL;
 
-/* mysqlname_parse - parse mysql configuration file */
+/* internal function declarations */
+static PLMYSQL *plmysql_init(char *hostnames[], int);
+static MYSQL_RES *plmysql_query(PLMYSQL *, const char *, char *, char *, char *);
+static void plmysql_dealloc(PLMYSQL *);
+static void plmysql_down_host(HOST *);
+static void plmysql_connect_single(HOST *, char *, char *, char *);
+static int plmysql_ready_reconn(HOST);
+static void dict_mysql_update(DICT *, const char *, const char *);
+static const char *dict_mysql_lookup(DICT *, const char *);
+DICT   *dict_mysql_open(const char *, int, int);
+static void dict_mysql_close(DICT *);
+static MYSQL_NAME *mysqlname_parse(const char *);
+static HOST host_init(char *);
+
+
+
+/**********************************************************************
+ * public interface dict_mysql_lookup
+ * find database entry return 0 if no alias found, set dict_errno
+ * on errors to DICT_ERRBO_RETRY and set dict_errno to 0 on success
+ *********************************************************************/
+static const char *dict_mysql_lookup(DICT *dict, const char *name)
+{
+    MYSQL_RES *query_res;
+    MYSQL_ROW row;
+    DICT_MYSQL *dict_mysql;
+    PLMYSQL *pldb;
+    static VSTRING *result;
+    static VSTRING *query = 0;
+    int     i,
+            numrows;
+    char   *name_escaped = 0;
 
+    dict_mysql = (DICT_MYSQL *) dict;
+    pldb = dict_mysql->pldb;
+    /* initialization  for query */
+    query = vstring_alloc(24);
+    vstring_strcpy(query, "");
+    if ((name_escaped = (char *) mymalloc((sizeof(char) * (strlen(name) * 2) +1))) == NULL) {
+       msg_fatal("dict_mysql_lookup: out of memory.");
+    }
+    /* prepare the query */
+    mysql_escape_string(name_escaped, name, (unsigned int) strlen(name));
+    vstring_sprintf(query, "select %s from %s where %s = '%s' %s", dict_mysql->name->select_field,
+       dict_mysql->name->table, dict_mysql->name->where_field, name_escaped,
+                   dict_mysql->name->additional_conditions);
+    if (msg_verbose)
+       msg_info("dict_mysql_lookup using sql query: %s", vstring_str(query));
+    /* free mem associated with preparing the query */
+    myfree(name_escaped);
+    /* do the query - set dict_errno & cleanup if there's an error */
+    if ((query_res = plmysql_query(pldb,
+                                  vstring_str(query),
+                                  dict_mysql->name->dbname,
+                                  dict_mysql->name->username,
+                                  dict_mysql->name->password)) == 0) {
+       dict_errno = DICT_ERR_RETRY;
+       vstring_free(query);
+       return 0;
+    }
+    dict_errno = 0;
+    /* free the vstring query */
+    vstring_free(query);
+    numrows = mysql_num_rows(query_res);
+    if (msg_verbose)
+       msg_info("dict_mysql_lookup: retrieved %d rows", numrows);
+    if (numrows == 0) {
+       mysql_free_result(query_res);
+       return 0;
+    }
+    if (result == 0)
+       result = vstring_alloc(10);
+    vstring_strcpy(result, "");
+    for (i = 0; i < numrows; i++) {
+       row = mysql_fetch_row(query_res);
+       if (msg_verbose > 1)
+           msg_info("dict_mysql_lookup: retrieved row: %d: %s", i, row[0]);
+       if (i > 0)
+           vstring_strcat(result, ",");
+       vstring_strcat(result, row[0]);
+    }
+    mysql_free_result(query_res);
+    return vstring_str(result);
+}
+
+/*
+ * plmysql_query - process a MySQL query.  Return MYSQL_RES* on success.
+ *                On failure, log failure and try other db instances.
+ *                on failure of all db instances, return 0;
+ *                close unnecessary active connections
+ */
+
+static MYSQL_RES *plmysql_query(PLMYSQL *PLDB,
+                                       const char *query,
+                                       char *dbname,
+                                       char *username,
+                                       char *password)
+{
+    int     i;
+    HOST   *host;
+    MYSQL_RES *res = 0;
+
+    for (i = 0; i < PLDB->len_hosts; i++) {
+       /* can't deal with typing or reading PLDB->db_hosts[i] over & over */
+       host = &(PLDB->db_hosts[i]);
+       if (msg_verbose > 1)
+           msg_info("dict_mysql: trying host %s stat %d, last res %p", host->hostname, host->stat, res);
+
+       /* answer already found */
+       if (res != 0 && host->stat == STATACTIVE) {
+           msg_info("dict_mysql: closing unnessary connection to %s", host->hostname);
+           mysql_close(&(host->db));           /* also frees memory, have to
+                                                * reallocate it */
+           host->db = *((MYSQL *) mymalloc(sizeof(MYSQL)));
+           plmysql_down_host(host);
+       }
+       /* try to connect for the first time if we don't have a result yet */
+       if (res == 0 && host->stat == STATUNTRIED) {
+           msg_info("dict_mysql: attempting to connect to host %s", host->hostname);
+           plmysql_connect_single(host, dbname, username, password);
+       }
+
+       /*
+        * try to reconnect if we don't have an answer and the host had a
+        * prob in the past and it's time for it to reconnect
+        */
+       if (res == 0 && host->stat == STATFAIL && (plmysql_ready_reconn(*host))) {
+           msg_warn("dict_mysql: attempting to reconnect to host %s", host->hostname);
+           plmysql_connect_single(host, dbname, username, password);
+       }
+
+       /*
+        * if we don't have a result and the current host is marked active,
+        * try the query.  If the query fails, mark the host STATFAIL
+        */
+       if (res == 0 && host->stat == STATACTIVE) {
+           if (!(mysql_query(&(host->db), query))) {
+               if ((res = mysql_store_result(&(host->db))) == 0) {
+                   msg_warn("%s", mysql_error(&(host->db)));
+                   plmysql_down_host(host);
+               } else {
+                   if (msg_verbose)
+                       msg_info("dict_mysql: successful query from host %s", host->hostname);
+               }
+           } else {
+               msg_warn("%s", mysql_error(&(host->db)));
+               plmysql_down_host(host);
+           }
+       }
+    }
+    return res;
+}
+
+/*
+ * plmysql_connect_single -
+ * used to reconnect to a single database when one is down or none is
+ * connected yet. Log all errors and set the stat field of host accordingly
+ */
+static void plmysql_connect_single(HOST *host, char *dbname, char *username, char *password)
+{
+    if (mysql_connect(&(host->db), host->hostname, username, password)) {
+       if (mysql_select_db(&(host->db), dbname) == 0) {
+           msg_info("dict_mysql: successful connection to host %s", host->hostname);
+           host->stat = STATACTIVE;
+       } else {
+           plmysql_down_host(host);
+           msg_warn("%s", mysql_error(&(host->db)));
+       }
+    } else {
+       plmysql_down_host(host);
+       msg_warn("%s", mysql_error(&(host->db)));
+    }
+}
+
+/*
+ * plmysql_down_host - mark a HOST down update ts if marked down
+ * for the first time so that we'll know when to retry the connection
+ */
+static void plmysql_down_host(HOST *host)
+{
+    if (host->stat != STATFAIL) {
+       host->ts = time(&(host->ts));
+       host->stat = STATFAIL;
+    }
+}
+
+/*
+ * plmysql_ready_reconn -
+ * given a downed HOST, return whether or not it should retry connection
+ */
+static int plmysql_ready_reconn(HOST host)
+{
+    time_t  t;
+    long    now;
+
+    now = (long) time(&t);
+    if (msg_verbose > 1) {
+       msg_info("dict_mysql: plmysql_ready_reconn(): now is %d", now);
+       msg_info("dict_mysql: plmysql_ready_reconn(): ts is %d", (long) host.ts);
+       msg_info("dict_mysql: plmysql_ready_reconn(): RETRY_CONN_INTV is %d", RETRY_CONN_INTV);
+       if ((now - ((long) host.ts)) >= RETRY_CONN_INTV) {
+           msg_info("dict_mysql: plymsql_ready_reconn(): returning TRUE");
+           return 1;
+       } else {
+           msg_info("dict_mysql: plymsql_ready_reconn(): returning FALSE");
+           return 0;
+       }
+    } else {
+       if ((now - ((long) host.ts)) >= RETRY_CONN_INTV)
+           return 1;
+       return 0;
+    }
+}
+
+/**********************************************************************
+ * public interface dict_mysql_open
+ *    create association with database with appropriate values
+ *    parse the map's config file
+ *    allocate memory
+ **********************************************************************/
+DICT   *dict_mysql_open(const char *name, int unused_flags, int unused_dict_flags)
+{
+    DICT_MYSQL *dict_mysql;
+    int     connections;
+
+    dict_mysql = (DICT_MYSQL *) mymalloc(sizeof(DICT_MYSQL));
+    dict_mysql->dict.lookup = dict_mysql_lookup;
+    dict_mysql->dict.update = dict_mysql_update;
+    dict_mysql->dict.close = dict_mysql_close;
+    dict_mysql->dict.fd = -1;                  /* there's no file descriptor
+                                                * for locking */
+    dict_mysql->name = (MYSQL_NAME *) mymalloc(sizeof(MYSQL_NAME));
+    dict_mysql->name = mysqlname_parse(name);
+    dict_mysql->pldb = plmysql_init(dict_mysql->name->hostnames,
+                                   dict_mysql->name->len_hosts);
+    if (dict_mysql->pldb == NULL)
+       msg_fatal("couldn't intialize pldb!\n");
+    dict_register(name, (DICT *) dict_mysql);
+    return &dict_mysql->dict;
+}
+
+/* mysqlname_parse - parse mysql configuration file */
 static MYSQL_NAME *mysqlname_parse(const char *mysqlcf_path)
 {
     int     i;
@@ -107,14 +371,14 @@ static MYSQL_NAME *mysqlname_parse(const char *mysqlcf_path)
     else
        name->username = mystrdup(nameval);
     if (msg_verbose)
-       msg_info("dict_mysql_parse: set username to '%s'", name->username);
+       msg_info("mysqlname_parse(): set username to '%s'", name->username);
     /* password lookup */
     if ((nameval = (char *) dict_lookup("mysql_options", "password")) == NULL)
        name->password = mystrdup("");
     else
        name->password = mystrdup(nameval);
     if (msg_verbose)
-       msg_info("dict_mysql_parse: set password to '%s'", name->password);
+       msg_info("mysqlname_parse(): set password to '%s'", name->password);
 
     /* database name lookup */
     if ((nameval = (char *) dict_lookup("mysql_options", "dbname")) == NULL)
@@ -122,7 +386,7 @@ static MYSQL_NAME *mysqlname_parse(const char *mysqlcf_path)
     else
        name->dbname = mystrdup(nameval);
     if (msg_verbose)
-       msg_info("mysql_name_parse: set database name to '%s'", name->dbname);
+       msg_info("mysqlname_parse(): set database name to '%s'", name->dbname);
 
     /* table lookup */
     if ((nameval = (char *) dict_lookup("mysql_options", "table")) == NULL)
@@ -130,7 +394,7 @@ static MYSQL_NAME *mysqlname_parse(const char *mysqlcf_path)
     else
        name->table = mystrdup(nameval);
     if (msg_verbose)
-       msg_info("mysql_name_parse: set table name to '%s'", name->table);
+       msg_info("mysqlname_parse(): set table name to '%s'", name->table);
 
     /* select field lookup */
     if ((nameval = (char *) dict_lookup("mysql_options", "select_field")) == NULL)
@@ -138,7 +402,7 @@ static MYSQL_NAME *mysqlname_parse(const char *mysqlcf_path)
     else
        name->select_field = mystrdup(nameval);
     if (msg_verbose)
-       msg_info("mysql_name_parse: set select_field to '%s'", name->select_field);
+       msg_info("mysqlname_parse(): set select_field to '%s'", name->select_field);
 
     /* where field lookup */
     if ((nameval = (char *) dict_lookup("mysql_options", "where_field")) == NULL)
@@ -146,7 +410,7 @@ static MYSQL_NAME *mysqlname_parse(const char *mysqlcf_path)
     else
        name->where_field = mystrdup(nameval);
     if (msg_verbose)
-       msg_info("mysql_name_parse: set where_field to '%s'", name->where_field);
+       msg_info("mysqlname_parse(): set where_field to '%s'", name->where_field);
 
     /* additional conditions */
     if ((nameval = (char *) dict_lookup("mysql_options", "additional_conditions")) == NULL)
@@ -154,7 +418,7 @@ static MYSQL_NAME *mysqlname_parse(const char *mysqlcf_path)
     else
        name->additional_conditions = mystrdup(nameval);
     if (msg_verbose)
-       msg_info("mysql_name_parse: set additional_conditions to '%s'", name->additional_conditions);
+       msg_info("mysqlname_parse(): set additional_conditions to '%s'", name->additional_conditions);
 
     /* mysql server hosts */
     if ((nameval = (char *) dict_lookup("mysql_options", "hosts")) == NULL)
@@ -167,7 +431,7 @@ static MYSQL_NAME *mysqlname_parse(const char *mysqlcf_path)
 
     if (hosts_argv->argc == 0) {               /* no hosts specified,
                                                 * default to 'localhost' */
-       msg_info("mysql_name_parse: no hostnames specified, defaulting to 'localhost'");
+       msg_info("mysqlname_parse(): no hostnames specified, defaulting to 'localhost'");
        name->len_hosts = 1;
        name->hostnames = (char **) mymalloc(sizeof(char *));
        name->hostnames[0] = mystrdup("localhost");
@@ -178,7 +442,8 @@ static MYSQL_NAME *mysqlname_parse(const char *mysqlcf_path)
        for (i = 0; hosts_argv->argv[i] != NULL; i++) {
            name->hostnames[i] = mystrdup(hosts_argv->argv[i]);
            if (msg_verbose)
-               msg_info("adding host '%s' to list of mysql server hosts", name->hostnames[i]);
+               msg_info("mysqlname_parse(): adding host '%s' to list of mysql server hosts",
+                        name->hostnames[i]);
        }
     }
     myfree(hosts);
@@ -186,124 +451,31 @@ static MYSQL_NAME *mysqlname_parse(const char *mysqlcf_path)
     return name;
 }
 
-/* dict_mysql_lookup - find database entry return 0 if no alias found */
-static const char *dict_mysql_lookup(DICT *dict, const char *name)
-{
-    MYSQL_RES *query_res;
-    MYSQL_ROW row;
-    int     i,
-            numrows;
-    static VSTRING *result;
-    static VSTRING *query = 0;
-    char   *name_escaped = 0;
-    DICT_MYSQL *dict_mysql;
-    PLMYSQL *pldb;
 
-    dict_mysql = (DICT_MYSQL *) dict;
-    pldb = dict_mysql->pldb;
-    /* initialization  for query */
-    query = vstring_alloc(24);
-    vstring_strcpy(query, "");
-    if ((name_escaped = (char *) mymalloc((sizeof(char) * (strlen(name) * 2) +1))) == NULL) {
-       msg_fatal("dict_mysql_lookup: out of memory.");
-    }
-    /* prepare the query */
-    mysql_escape_string(name_escaped, name, (unsigned int) strlen(name));
-    vstring_sprintf(query, "select %s from %s where %s = '%s' %s", dict_mysql->name->select_field,
-       dict_mysql->name->table, dict_mysql->name->where_field, name_escaped,
-                   dict_mysql->name->additional_conditions);
-    if (msg_verbose)
-       msg_info("dict_mysql_lookup using sql query: %s", vstring_str(query));
-    /* free mem associated with preparing the query */
-    myfree(name_escaped);
-    /* do the query */
-    if ((query_res = plmysql_query(pldb, vstring_str(query))) == NULL) {
-       dict_errno = DICT_ERR_RETRY;
-       vstring_free(query);
-       return 0;
-    }
-    dict_errno = 0;
-    /* free the vstring query */
-    vstring_free(query);
-    numrows = mysql_num_rows(query_res);
-    if (msg_verbose)
-       msg_info("dict_mysql_lookup: retrieved %d rows", numrows);
-    if (numrows == 0) {
-       mysql_free_result(query_res);
-       return 0;
-    }
-    if (result == 0)
-       result = vstring_alloc(10);
-    vstring_strcpy(result, "");
-    for (i = 0; i < numrows; i++) {
-       row = mysql_fetch_row(query_res);
-       if (msg_verbose > 1)
-           msg_info("dict_mysql_lookup: retrieved row: %d: %s", i, row[0]);
-       if (i > 0)
-           vstring_strcat(result, ",");
-       vstring_strcat(result, row[0]);
-    }
-    mysql_free_result(query_res);
-    return vstring_str(result);
-}
-
-/* dict_mysql_close - unregister, disassociate from database */
-static void dict_mysql_close(DICT *dict)
+/*
+ * plmysql_init - initalize a MYSQL database.
+ *               Return NULL on failure, or a PLMYSQL * on success.
+ */
+static PLMYSQL *plmysql_init(char *hostnames[],
+                                    int len_hosts)
 {
+    PLMYSQL *PLDB;
+    MYSQL  *dbs;
     int     i;
-    DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict;
+    HOST    host;
 
-    plmysql_dealloc(dict_mysql->pldb);
-    myfree(dict_mysql->name->username);
-    myfree(dict_mysql->name->password);
-    myfree(dict_mysql->name->dbname);
-    myfree(dict_mysql->name->table);
-    myfree(dict_mysql->name->select_field);
-    myfree(dict_mysql->name->where_field);
-    myfree(dict_mysql->name->additional_conditions);
-    for (i = 0; i < dict_mysql->name->len_hosts; i++) {
-       myfree(dict_mysql->name->hostnames[i]);
+    if ((PLDB = (PLMYSQL *) mymalloc(sizeof(PLMYSQL))) == NULL) {
+       msg_fatal("mymalloc of pldb failed");
     }
-    myfree((char *) dict_mysql->name);
-}
-
-/* dict_mysql_update - add or update table entry */
-static void dict_mysql_update(DICT *dict, const char *unused_name, const char *unused_value)
-{
-    DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict;
-
-    msg_fatal("dict_mysql_update: attempt to update mysql database");
+    PLDB->len_hosts = len_hosts;
+    if ((PLDB->db_hosts = (HOST *) mymalloc(sizeof(HOST) * len_hosts)) == NULL)
+       return NULL;
+    for (i = 0; i < len_hosts; i++) {
+       PLDB->db_hosts[i] = host_init(hostnames[i]);
+    }
+    return PLDB;
 }
 
-/* dict_mysql_open - create association with database */
-DICT   *dict_mysql_open(const char *name, int unused_flags, int unused_dict_flags)
-{
-    DICT_MYSQL *dict_mysql;
-    int     connections;
-
-    dict_mysql = (DICT_MYSQL *) mymalloc(sizeof(DICT_MYSQL));
-    dict_mysql->dict.lookup = dict_mysql_lookup;
-    dict_mysql->dict.update = dict_mysql_update;
-    dict_mysql->dict.close = dict_mysql_close;
-    dict_mysql->dict.fd = -1;                  /* there's no file descriptor
-                                                * for locking */
-    dict_mysql->name = (MYSQL_NAME *) mymalloc(sizeof(MYSQL_NAME));
-    dict_mysql->name = mysqlname_parse(name);
-    dict_mysql->pldb = plmysql_init(dict_mysql->name->dbname,
-                                   dict_mysql->name->hostnames,
-                                   dict_mysql->name->len_hosts);
-    if (dict_mysql->pldb == NULL)
-       msg_fatal("couldn't intialize pldb!\n");
-    connections = plmysql_connect(dict_mysql->pldb, dict_mysql->name->username,
-                                 dict_mysql->name->password);
-    if (connections == 0)
-       /* the mysql lookup mechanism will try to reconnect anyway ... */
-       msg_warn("couldn't connect pldb to any database instances");
-    else
-       msg_info("pldb connected to %d database instances", connections);
-    dict_register(name, (DICT *) dict_mysql);
-    return &dict_mysql->dict;
-}
 
 /* host_init - initialize HOST structure */
 static HOST host_init(char *hostname)
@@ -320,34 +492,31 @@ static HOST host_init(char *hostname)
     return host;
 }
 
-/*
- * plmysql_init - initalize a MYSQL database.
- *               Return NULL on failure, or a PLMYSQL * on success.
- */
-PLMYSQL *plmysql_init(char *dbname,
-                             char *hostnames[],
-                             int len_hosts)
+/**********************************************************************
+ * public interface dict_mysql_close
+ * unregister, disassociate from database, freeing appropriate memory
+ **********************************************************************/
+static void dict_mysql_close(DICT *dict)
 {
-    PLMYSQL *PLDB;
-    MYSQL  *dbs;
     int     i;
-    HOST    host;
+    DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict;
 
-    if ((PLDB = (PLMYSQL *) mymalloc(sizeof(PLMYSQL))) == NULL) {
-       msg_fatal("mymalloc of pldb failed");
-    }
-    PLDB->dbname = dbname;
-    PLDB->len_hosts = len_hosts;
-    if ((PLDB->db_hosts = (HOST *) mymalloc(sizeof(HOST) * len_hosts)) == NULL)
-       return NULL;
-    for (i = 0; i < len_hosts; i++) {
-       PLDB->db_hosts[i] = host_init(hostnames[i]);
+    plmysql_dealloc(dict_mysql->pldb);
+    myfree(dict_mysql->name->username);
+    myfree(dict_mysql->name->password);
+    myfree(dict_mysql->name->dbname);
+    myfree(dict_mysql->name->table);
+    myfree(dict_mysql->name->select_field);
+    myfree(dict_mysql->name->where_field);
+    myfree(dict_mysql->name->additional_conditions);
+    for (i = 0; i < dict_mysql->name->len_hosts; i++) {
+       myfree(dict_mysql->name->hostnames[i]);
     }
-    return PLDB;
+    myfree((char *) dict_mysql->name);
 }
 
 /* plmysql_dealloc - free memory associated with PLMYSQL close databases */
-void    plmysql_dealloc(PLMYSQL *PLDB)
+static void plmysql_dealloc(PLMYSQL *PLDB)
 {
     int     i;
 
@@ -359,96 +528,16 @@ void    plmysql_dealloc(PLMYSQL *PLDB)
     myfree((char *) (PLDB));
 }
 
-/* plmysql_down_host - down a HOST * */
-inline void plmysql_down_host(HOST *host)
-{
-    if (host->stat != STATFAIL)
-       host->ts = time(&(host->ts));
-    host->stat = STATFAIL;
-}
-
-/* plmysql_connect_single -
- * used to reconnect to a single database when one is down and as a helper for
- * plmysql_connect
- */
-int     plmysql_connect_single(PLMYSQL *PLDB, int host)
-{
-    if ((mysql_connect(&(PLDB->db_hosts[host].db), PLDB->db_hosts[host].hostname,
-                      PLDB->username, PLDB->password))) {
-       if (mysql_select_db(&(PLDB->db_hosts[host].db), PLDB->dbname) == 0) {
-           PLDB->db_hosts[host].stat = STATACTIVE;
-           return 1;
-       } else {
-           plmysql_down_host(&(PLDB->db_hosts[host]));
-           msg_warn("%s", mysql_error(&PLDB->db_hosts[host].db));
-       }
-    } else {
-       plmysql_down_host(&(PLDB->db_hosts[host]));
-       msg_warn("%s", mysql_error(&PLDB->db_hosts[host].db));
-    }
-    return 0;
-}
-
-/*
- * plmysql_connect -
- * given a PLMYSQL struct PLDB *, connect it and select db.
- * return  the number of databases successfully connected (0 for failure)
- */
-int     plmysql_connect(PLMYSQL *PLDB, char *username, char *password)
-{
-    int     i,
-            res;
-
-    res = 0;
-
-    PLDB->username = username;
-    PLDB->password = password;
-
-    for (i = 0; i < PLDB->len_hosts; i++) {
-       res = res + plmysql_connect_single(PLDB, i);
-    }
-    return res;
-}
-
-/* plmysql_ready_reconn -
-   given a downed HOST, return whether or not it should retry connection
-*/
-int     plmysql_ready_reconn(HOST host)
-{
-    time_t  t;
-    long    now;
-
-    now = (long) time(&t);
-    if ((now - ((long) host.ts)) >= RETRY_CONN_INTV)
-       return 1;
-    return 0;
-}
-
-/*
- * plmysql_query - process a MySQL query.  Return 0 on success.
- *                On failure, log failure and try other db instances.
- */
 
-MYSQL_RES *plmysql_query(PLMYSQL *PLDB, const char *query)
+/**********************************************************************
+ * public interface dict_mysql_update - add or update table entry
+ *
+ *********************************************************************/
+static void dict_mysql_update(DICT *dict, const char *unused_name, const char *unused_value)
 {
-    int     i;
-    MYSQL_RES *res;
+    DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict;
 
-    for (i = 0; i < PLDB->len_hosts; i++) {
-       if ((PLDB->db_hosts[i].stat != STATACTIVE) &&
-           (plmysql_ready_reconn(PLDB->db_hosts[i]))) {
-           msg_warn("attempting to reconnect to host");
-           plmysql_connect_single(PLDB, i);
-           continue;
-       }
-       if ((!(mysql_query(&(PLDB->db_hosts[i].db), query))) && \
-           (res = mysql_store_result(&(PLDB->db_hosts[i].db)))) {
-           return res;
-       }
-       msg_warn("%s", mysql_error(&PLDB->db_hosts[i].db));
-       plmysql_down_host(&(PLDB->db_hosts[i]));
-    }
-    return NULL;
+    msg_fatal("dict_mysql_update: attempt to update mysql database");
 }
 
 #endif
index 601ccda34cc107a0f99ab6a5f5cf3644fdc4fcd3..fdb9395d32376dd5959fa4def432f761eec10c96 100644 (file)
@@ -6,41 +6,9 @@
 #define STATACTIVE 0
 #define STATFAIL 1
 #define STATUNTRIED 2
-#define RETRY_CONN_INTV 300            /* 5 minutes */
+#define RETRY_CONN_INTV 60             /* 1 minute */
 
 extern DICT *dict_mysql_open(const char *name, int unused_flags, int dict_flags);
 
-typedef struct {
-    char   *hostname;
-    int     stat;                      /* STATUNTRIED | STATFAIL | STATCUR */
-    time_t  ts;                                /* used for attempting reconnection
-                                        * every so often if a host is down */
-    MYSQL   db;
-} HOST;
-
-
-typedef struct {
-    char   *username;                  /* login for database */
-    char   *password;                  /* password for database */
-    char   *dbname;                    /* the name of the database on all
-                                        * the servers */
-    HOST   *db_hosts;                  /* the hosts on which the databases
-                                        * reside */
-    int     len_hosts;                 /* number of hosts */
-} PLMYSQL;
-
-extern PLMYSQL *plmysql_init(char *dbname, char *hostnames[], int len_hosts);
-
-extern int plmysql_connect(PLMYSQL *PLDB, char *username, char *password);
-
-MYSQL_RES *plmysql_query(PLMYSQL *PLDB, const char *query);
-
-void    plmysql_dealloc(PLMYSQL *PLDB);
-
-inline void plmysql_down_host(HOST *host);
-
-int     plmysql_connect_single(PLMYSQL *PLDB, int host);
-
-int     plmysql_ready_reconn(HOST host);
 
 #endif