2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-09-03 15:45:24 +00:00

snapshot-19991209

This commit is contained in:
Wietse Venema
1999-12-09 00:00:00 -05:00
parent 5b4600231a
commit 5587a4cbad
24 changed files with 770 additions and 439 deletions

View 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 /etc/postfix/master.cf and type "postfix reload". This will cause
a lot of activity to be logged to the syslog daemon. 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 Some systems allow you to inspect a running process with a system
call tracer. For example: call tracer. For example:
@@ -41,12 +41,30 @@ See your system documentation for details.
Tracing a running process can give valuable information about what Tracing a running process can give valuable information about what
a process is attempting to do. This is as much information as you a process is attempting to do. This is as much information as you
can get without running an interactive debugger program, as described 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 5 - Automatically tracing a Postfix daemon process
a Postfix daemon. ==================================================
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, 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 Whenever the suspect daemon process is started, a debugger window
pops up and you can watch in detail what happens. 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 Sometimes the behavior exhibit by Postfix just does not match the

View File

@@ -3317,15 +3317,43 @@ Apologies for any names omitted.
19991207 19991207
Performance: the queue manager now frees in-memory recipients Performance: one message with many recipients no longer
as soon as a message is delivered to one destination, rather stops other mail from being delivered. The queue manager
than waiting until all in-memory recipients of that message now frees in-memory recipients as soon as a message is
have been tried. This means that one message with many delivered to one destination, rather than waiting until
recipients no longer stops other mail from being delivered. all in-memory destinations of that message have been tried.
Patch by Patrik Rak @ ein.cz. Files: qmgr/qmgr_entry.c, Patch by Patrik Rak @ ein.cz. Files: qmgr/qmgr_entry.c,
qmgr/qmgr_message.c. qmgr/qmgr_message.c.
Performance: when delivering a huge list of recipients, Performance: when delivering mail to a huge list of
the queue manager now reads new recipients from queue file recipients, the queue manager now reads more recipients
before delivery concurrency starts dropping. Files: from the queue file before delivery concurrency starts
qmgr/qmgr_entry.c, qmgr/qmgr_message.c. 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.

View 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 - 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. need to add a "domain.name local:" entry in your transport_maps.
See the html/faq.html sections for firewalls and intranets. 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 - It is now relatively safe to configure 550 status codes for the

View File

@@ -222,9 +222,9 @@ mail_owner = postfix
# DELIVERY TO MAILBOX # DELIVERY TO MAILBOX
# #
# The home_mailbox parameter specifies the optional pathname of a # The home_mailbox parameter specifies the optional pathname of a
# mailbox relative to a user's home directory. The default is to # mailbox file relative to a user's home directory. The default
# deliver to the UNIX-style /var/spool/mail/user or /var/mail/user. # mailbox file is /var/spool/mail/user or /var/mail/user. Specify
# Specify "Maildir/" for qmail-style delivery (the / is required). # "Maildir/" for qmail-style delivery (the / is required).
# #
#home_mailbox = Mailbox #home_mailbox = Mailbox
#home_mailbox = Maildir/ #home_mailbox = Maildir/
@@ -298,12 +298,14 @@ mail_owner = postfix
#header_checks = regexp:/etc/postfix/filename #header_checks = regexp:/etc/postfix/filename
#header_checks = pcre:/etc/postfix/filename #header_checks = pcre:/etc/postfix/filename
# The relay_domains parameter restricts what domains (and subdomains # The relay_domains parameter restricts what client hostname domains
# thereof) this mail system will relay mail from or to. See the # (and subdomains thereof) this mail system will relay mail from,
# smtpd_recipient_restrictions restriction in the file sample-smtpd.cf. # 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 # By default, Postfix relays mail only from clients or to destinations
# $mydestination, or in the optional virtual domain list. # in or below $mydestination, or in the optional virtual domain list.
# #
# Specify a list of hosts or domains, /file/name patterns or type:name # Specify a list of hosts or domains, /file/name patterns or type:name
# lookup tables, separated by commas and/or whitespace. Continue # lookup tables, separated by commas and/or whitespace. Continue

View File

@@ -66,9 +66,9 @@ default_privs = nobody
# #
# The home_mailbox parameter specifies the optional pathname of a # The home_mailbox parameter specifies the optional pathname of a
# mailbox relative to a user's home directory. The default is to # mailbox file relative to a user's home directory. The default
# deliver to the UNIX-style /var/spool/mail/user or /var/mail/user. # mailbox file is /var/spool/mail/user or /var/mail/user. Specify
# Specify "Maildir/" for qmail-style delivery (the / is required). # "Maildir/" for qmail-style delivery (the / is required).
# #
# home_mailbox = Mailbox # home_mailbox = Mailbox
# home_mailbox = Maildir/ # home_mailbox = Maildir/

View File

@@ -181,7 +181,7 @@ smtpd_sender_restrictions =
# #
# The default is to permit any destination from clients that match # The default is to permit any destination from clients that match
# $mynetworks, and to otherwise permit only mail from clients or to # $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: # The following restrictions are available:
# #
@@ -191,8 +191,8 @@ smtpd_sender_restrictions =
# reject_invalid_hostname: reject HELO hostname with bad syntax. # reject_invalid_hostname: reject HELO hostname with bad syntax.
# reject_unknown_hostname: reject HELO hostname without DNS A or MX record. # reject_unknown_hostname: reject HELO hostname without DNS A or MX record.
# reject_unknown_sender_domain: reject sender domain without 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 # check_relay_domains: permit only mail from clients/to domains matching
or to the local machine. # $relay_domains, or to the local machine.
# permit_auth_destination: permit mail to self or to $relay_domains. # 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_destination: reject mail not to self or to $relay_domains.
# reject_unauth_pipelining: reject mail from improperly pipelining spamware # 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 maps_rbl_domains = rbl.maps.vix.com
# The relay_domains parameter restricts what domains (and subdomains # The relay_domains parameter restricts what client hostname domains
# thereof) this mail system will relay mail from or to. # (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 # By default, Postfix relays mail only from clients or to destinations
# $mydestination, or in the optional virtual domain list. # in or below $mydestination, or in the optional virtual domain list.
# #
# Specify a list of hosts or domains, /file/name patterns or type:name # Specify a list of hosts or domains, /file/name patterns or type:name
# lookup tables, separated by commas and/or whitespace. Continue # lookup tables, separated by commas and/or whitespace. Continue

View File

@@ -17,6 +17,14 @@
/* BH_TABLE *dup_filter; /* BH_TABLE *dup_filter;
/* char *format; /* 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) /* void been_here_free(dup_filter)
/* BH_TABLE *dup_filter; /* BH_TABLE *dup_filter;
/* DESCRIPTION /* DESCRIPTION
@@ -34,6 +42,9 @@
/* not found. The result is non-zero (true) if the formatted result was /* not found. The result is non-zero (true) if the formatted result was
/* found, zero (false) otherwise. /* 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. /* been_here_free() releases storage for a duplicate filter.
/* /*
/* Arguments: /* Arguments:
@@ -173,3 +184,65 @@ int been_here_fixed(BH_TABLE *dup_filter, const char *string)
return (status); 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);
}

View File

@@ -32,6 +32,8 @@ extern BH_TABLE *been_here_init(int, int);
extern void been_here_free(BH_TABLE *); extern void been_here_free(BH_TABLE *);
extern int been_here_fixed(BH_TABLE *, const char *); extern int been_here_fixed(BH_TABLE *, const char *);
extern int been_here(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 /* LICENSE
/* .ad /* .ad

View File

@@ -15,7 +15,7 @@
* Version of this program. * Version of this program.
*/ */
#define VAR_MAIL_VERSION "mail_version" #define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "Snapshot-19991207" #define DEF_MAIL_VERSION "Snapshot-19991209"
extern char *var_mail_version; extern char *var_mail_version;
/* LICENSE /* LICENSE

View File

@@ -9,7 +9,7 @@ POSTALIAS(1) POSTALIAS(1)
postalias - Postfix alias database maintenance postalias - Postfix alias database maintenance
<b>SYNOPSIS</b> <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> ... [<i>file_type</i>:]<i>file_name</i> ...
<b>DESCRIPTION</b> <b>DESCRIPTION</b>
@@ -26,15 +26,25 @@ POSTALIAS(1) POSTALIAS(1)
Options: 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> <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. 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 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>. 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 <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. first value found on the standard output stream.
The exit status is non-zero if the requested infor- The exit status is non-zero if the requested infor-
@@ -49,16 +59,6 @@ POSTALIAS(1) POSTALIAS(1)
Arguments: 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) 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>dbm</b> databases.
<b>hash</b> The output is a hashed file, named <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="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. <a href="sendmail.1.html">sendmail(1)</a> mail posting and compatibility interface.
2
POSTALIAS(1) POSTALIAS(1)
<b>LICENSE</b> <b>LICENSE</b>
The Secure Mailer license must be distributed with this The Secure Mailer license must be distributed with this
software. software.
@@ -128,7 +151,50 @@ POSTALIAS(1) POSTALIAS(1)
2
3
</pre> </body> </html> </pre> </body> </html>

View File

@@ -9,7 +9,7 @@ POSTMAP(1) POSTMAP(1)
postmap - Postfix lookup table management postmap - Postfix lookup table management
<b>SYNOPSIS</b> <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> ... [<i>file_type</i>:]<i>file_name</i> ...
<b>DESCRIPTION</b> <b>DESCRIPTION</b>
@@ -45,20 +45,20 @@ POSTMAP(1) POSTMAP(1)
Options: 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> <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. 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 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>. 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) 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- <b>-v</b> Enable verbose logging for debugging purposes. Mul-
tiple <b>-v</b> options make the software increasingly tiple <b>-v</b> options make the software increasingly
verbose. verbose.
@@ -116,16 +126,6 @@ POSTMAP(1) POSTMAP(1)
<b>MAIL</b><i>_</i><b>VERBOSE</b> <b>MAIL</b><i>_</i><b>VERBOSE</b>
Enable verbose logging for debugging purposes. 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 2
@@ -137,6 +137,16 @@ POSTMAP(1) POSTMAP(1)
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> <b>AUTHOR(S)</b>
Wietse Venema Wietse Venema
IBM T.J. Watson Research IBM T.J. Watson Research
@@ -173,16 +183,6 @@ POSTMAP(1) POSTMAP(1)

View File

@@ -550,7 +550,7 @@ specifies the response code for rejected requests (default:
<dt> <b>permit_auth_destination</b> <dd> Ignore the client hostname. <dt> <b>permit_auth_destination</b> <dd> Ignore the client hostname.
Permit the request when the resolved destination address matches 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>. machine IP addresses, or <a href="#relay_domains"> $relay_domains</a>.
<p> <p>

View 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, * If this user includes (an alias of) herself in her own .forward file,
* deliver to the user instead. * deliver to the user instead.
*/ */
if (lookup_status >= 0 if (lookup_status >= 0) {
&& been_here(state.dup_filter, "forward %s", STR(path)) == 0) { if (been_here(state.dup_filter, "forward %s", STR(path)) == 0) {
state.msg_attr.exp_from = state.msg_attr.local; state.msg_attr.exp_from = state.msg_attr.local;
if (S_ISREG(st.st_mode) == 0) { if (S_ISREG(st.st_mode) == 0) {
msg_warn("file %s is not a regular file", STR(path)); msg_warn("file %s is not a regular file", STR(path));
} else if (st.st_uid != 0 && st.st_uid != usr_attr.uid) { } 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); msg_warn("file %s has bad owner uid %d", STR(path), st.st_uid);
} else if (st.st_mode & 002) { } else if (st.st_mode & 002) {
msg_warn("file %s is world writable", STR(path)); 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) { } 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)); msg_warn("cannot open file %s: %m", STR(path));
} else { } else {
close_on_exec(fd, CLOSE_ON_EXEC); close_on_exec(fd, CLOSE_ON_EXEC);
addr_count = 0; addr_count = 0;
fp = vstream_fdopen(fd, O_RDONLY); fp = vstream_fdopen(fd, O_RDONLY);
status = deliver_token_stream(state, usr_attr, fp, &addr_count); status = deliver_token_stream(state, usr_attr, fp, &addr_count);
if (vstream_fclose(fp)) if (vstream_fclose(fp))
msg_warn("close file %s: %m", STR(path)); msg_warn("close file %s: %m", STR(path));
if (addr_count > 0) if (addr_count > 0) {
forward_found = YES; 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 */
} }
/* /*

View File

@@ -177,9 +177,7 @@ int deliver_token_string(LOCAL_STATE state, USER_ATTR usr_attr,
if (addr->type == TOK822_ADDR) { if (addr->type == TOK822_ADDR) {
if (addr_count) if (addr_count)
(*addr_count)++; (*addr_count)++;
status = deliver_token(state, usr_attr, addr); status |= deliver_token(state, usr_attr, addr);
if (status != 0)
break;
} }
} }
tok822_free_tree(tree); tok822_free_tree(tree);

View File

@@ -9,8 +9,8 @@ Postfix alias database maintenance
.na .na
.nf .nf
.fi .fi
\fBpostalias\fR [\fB-ivw\fR] [\fB-c \fIconfig_dir\fR] [\fB-q \fIkey\fR] \fBpostalias\fR [\fB-Ninvw\fR] [\fB-c \fIconfig_dir\fR]
[\fIfile_type\fR:]\fIfile_name\fR ... [\fB-q \fIkey\fR] [\fIfile_type\fR:]\fIfile_name\fR ...
.SH DESCRIPTION .SH DESCRIPTION
.ad .ad
.fi .fi
@@ -25,12 +25,20 @@ entire database, in order to avoid surprises in spectator
programs. programs.
Options: 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" .IP "\fB-c \fIconfig_dir\fR"
Read the \fBmain.cf\fR configuration file in the named directory. Read the \fBmain.cf\fR configuration file in the named directory.
.IP \fB-i\fR .IP \fB-i\fR
Incremental mode. Read entries from standard input and do not Incremental mode. Read entries from standard input and do not
truncate an existing database. By default, \fBpostalias\fR creates truncate an existing database. By default, \fBpostalias\fR creates
a new database from the entries in \fBfile_name\fR. 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" .IP "\fB-q \fIkey\fR"
Search the specified maps for \fIkey\fR and print the first value Search the specified maps for \fIkey\fR and print the first value
found on the standard output stream. The exit status is non-zero found on the standard output stream. The exit status is non-zero

View File

@@ -9,7 +9,7 @@ Postfix lookup table management
.na .na
.nf .nf
.fi .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 ... [\fIfile_type\fR:]\fIfile_name\fR ...
.SH DESCRIPTION .SH DESCRIPTION
.ad .ad
@@ -44,12 +44,20 @@ special characters such as `#' or whitespace. The \fIkey\fR is mapped
to lowercase to make mapping lookups case insensitive. to lowercase to make mapping lookups case insensitive.
Options: 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" .IP "\fB-c \fIconfig_dir\fR"
Read the \fBmain.cf\fR configuration file in the named directory. Read the \fBmain.cf\fR configuration file in the named directory.
.IP \fB-i\fR .IP \fB-i\fR
Incremental mode. Read entries from standard input and do not Incremental mode. Read entries from standard input and do not
truncate an existing database. By default, \fBpostmap\fR creates truncate an existing database. By default, \fBpostmap\fR creates
a new database from the entries in \fBfile_name\fR. 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" .IP "\fB-q \fIkey\fR"
Search the specified maps for \fIkey\fR and print the first value Search the specified maps for \fIkey\fR and print the first value
found on the standard output stream. The exit status is non-zero found on the standard output stream. The exit status is non-zero

View File

@@ -5,8 +5,8 @@
/* Postfix alias database maintenance /* Postfix alias database maintenance
/* SYNOPSIS /* SYNOPSIS
/* .fi /* .fi
/* \fBpostalias\fR [\fB-ivw\fR] [\fB-c \fIconfig_dir\fR] [\fB-q \fIkey\fR] /* \fBpostalias\fR [\fB-Ninvw\fR] [\fB-c \fIconfig_dir\fR]
/* [\fIfile_type\fR:]\fIfile_name\fR ... /* [\fB-q \fIkey\fR] [\fIfile_type\fR:]\fIfile_name\fR ...
/* DESCRIPTION /* DESCRIPTION
/* The \fBpostalias\fR command creates or queries one or more Postfix /* The \fBpostalias\fR command creates or queries one or more Postfix
/* alias databases, or updates an existing one. The input and output /* alias databases, or updates an existing one. The input and output
@@ -19,12 +19,20 @@
/* programs. /* programs.
/* /*
/* Options: /* 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" /* .IP "\fB-c \fIconfig_dir\fR"
/* Read the \fBmain.cf\fR configuration file in the named directory. /* Read the \fBmain.cf\fR configuration file in the named directory.
/* .IP \fB-i\fR /* .IP \fB-i\fR
/* Incremental mode. Read entries from standard input and do not /* Incremental mode. Read entries from standard input and do not
/* truncate an existing database. By default, \fBpostalias\fR creates /* truncate an existing database. By default, \fBpostalias\fR creates
/* a new database from the entries in \fBfile_name\fR. /* 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" /* .IP "\fB-q \fIkey\fR"
/* Search the specified maps for \fIkey\fR and print the first value /* Search the specified maps for \fIkey\fR and print the first value
/* found on the standard output stream. The exit status is non-zero /* 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) 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); myname);
} }
@@ -346,11 +354,15 @@ int main(int argc, char **argv)
/* /*
* Parse JCL. * Parse JCL.
*/ */
while ((ch = GETOPT(argc, argv, "c:iq:vw")) > 0) { while ((ch = GETOPT(argc, argv, "Nc:inq:vw")) > 0) {
switch (ch) { switch (ch) {
default: default:
usage(argv[0]); usage(argv[0]);
break; break;
case 'N':
dict_flags |= DICT_FLAG_TRY1NULL;
dict_flags &= ~DICT_FLAG_TRY0NULL;
break;
case 'c': case 'c':
if (setenv(CONF_ENV_PATH, optarg, 1) < 0) if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
msg_fatal("out of memory"); msg_fatal("out of memory");
@@ -358,6 +370,10 @@ int main(int argc, char **argv)
case 'i': case 'i':
open_flags &= ~O_TRUNC; open_flags &= ~O_TRUNC;
break; break;
case 'n':
dict_flags |= DICT_FLAG_TRY0NULL;
dict_flags &= ~DICT_FLAG_TRY1NULL;
break;
case 'q': case 'q':
query = optarg; query = optarg;
break; break;

View File

@@ -638,6 +638,9 @@ int main(int argc, char **argv)
struct stat st; struct stat st;
int junk; int junk;
/*
* Be consistent with file permissions.
*/
umask(022); umask(022);
/* /*

View File

@@ -5,7 +5,7 @@
/* Postfix lookup table management /* Postfix lookup table management
/* SYNOPSIS /* SYNOPSIS
/* .fi /* .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 ... /* [\fIfile_type\fR:]\fIfile_name\fR ...
/* DESCRIPTION /* DESCRIPTION
/* The \fBpostmap\fR command creates or queries one or more Postfix /* The \fBpostmap\fR command creates or queries one or more Postfix
@@ -38,12 +38,20 @@
/* to lowercase to make mapping lookups case insensitive. /* to lowercase to make mapping lookups case insensitive.
/* /*
/* Options: /* 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" /* .IP "\fB-c \fIconfig_dir\fR"
/* Read the \fBmain.cf\fR configuration file in the named directory. /* Read the \fBmain.cf\fR configuration file in the named directory.
/* .IP \fB-i\fR /* .IP \fB-i\fR
/* Incremental mode. Read entries from standard input and do not /* Incremental mode. Read entries from standard input and do not
/* truncate an existing database. By default, \fBpostmap\fR creates /* truncate an existing database. By default, \fBpostmap\fR creates
/* a new database from the entries in \fBfile_name\fR. /* 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" /* .IP "\fB-q \fIkey\fR"
/* Search the specified maps for \fIkey\fR and print the first value /* Search the specified maps for \fIkey\fR and print the first value
/* found on the standard output stream. The exit status is non-zero /* 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) 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); myname);
} }
@@ -300,11 +308,15 @@ int main(int argc, char **argv)
/* /*
* Parse JCL. * Parse JCL.
*/ */
while ((ch = GETOPT(argc, argv, "c:iq:vw")) > 0) { while ((ch = GETOPT(argc, argv, "Nc:inq:vw")) > 0) {
switch (ch) { switch (ch) {
default: default:
usage(argv[0]); usage(argv[0]);
break; break;
case 'N':
dict_flags |= DICT_FLAG_TRY1NULL;
dict_flags &= ~DICT_FLAG_TRY0NULL;
break;
case 'c': case 'c':
if (setenv(CONF_ENV_PATH, optarg, 1) < 0) if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
msg_fatal("out of memory"); msg_fatal("out of memory");
@@ -312,6 +324,10 @@ int main(int argc, char **argv)
case 'i': case 'i':
open_flags &= ~O_TRUNC; open_flags &= ~O_TRUNC;
break; break;
case 'n':
dict_flags |= DICT_FLAG_TRY0NULL;
dict_flags &= ~DICT_FLAG_TRY1NULL;
break;
case 'q': case 'q':
query = optarg; query = optarg;
break; break;

View 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. * accordingly.
*/ */
qmgr_recipient_count -= entry->rcpt_list.len; 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 * Update the in-core message reference count. When the in-core message
* structure has no more references, dispose of the message. When the * structure has no more references, dispose of the message.
* in-core recipient count falls below some threshold and this message *
* has more recipients, read them from disk before concurrency starts to * When the in-core recipient count falls below some threshold and this
* drop. * message has more recipients, read more recipients before concurrency
* starts to drop.
*/ */
message->refcount--; message->refcount--;
if (message->refcount == 0) if (message->refcount == 0)

View 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 * may appear before or after the message content, so we keep reading
* from the queue file until we have enough recipients (rcpt_offset != 0) * from the queue file until we have enough recipients (rcpt_offset != 0)
* and until we know where the message content starts (data_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 { do {
if ((curr_offset = vstream_ftell(message->fp)) < 0) 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); message->data_size, "queue %s", message->queue_name);
} }
} else if (rec_type == REC_TYPE_RCPT) { } else if (rec_type == REC_TYPE_RCPT) {
#define TOTAL_RECIPIENT_COUNT (qmgr_recipient_count + message->rcpt_list.len) if (message->rcpt_list.len < var_qmgr_rcpt_limit) {
if (TOTAL_RECIPIENT_COUNT < var_qmgr_rcpt_limit) {
qmgr_rcpt_list_add(&message->rcpt_list, curr_offset, start); 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) if ((message->rcpt_offset = vstream_ftell(message->fp)) < 0)
msg_fatal("vstream_ftell %s: %m", msg_fatal("vstream_ftell %s: %m",
VSTREAM_PATH(message->fp)); VSTREAM_PATH(message->fp));

View File

@@ -224,20 +224,39 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
escaped_name = vstring_alloc(20); escaped_name = vstring_alloc(20);
filter_buf = vstring_alloc(30); 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) if (msg_verbose)
msg_info("%s: found wildcard in %s", myname, name); msg_info("%s: found character(s) in %s that must be escaped", myname, name);
for (sub = (char *) name; *sub != '\0'; sub++) { for (sub = (char *) name; sub != end; sub++) {
if (*sub == '*' || *sub == '\\') { switch (*sub) {
vstring_strncat(escaped_name, "\\", 1); case '*':
vstring_strncat(escaped_name, sub, 1); vstring_strcat(escaped_name, "\\2a");
} else { 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); vstring_strncat(escaped_name, sub, 1);
} }
} }
if (msg_verbose) 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 } else
vstring_strcpy(escaped_name, (char *) name); vstring_strcpy(escaped_name, (char *) name);

View File

@@ -12,11 +12,19 @@
/* int dummy; /* int dummy;
/* int unused_dict_flags; /* int unused_dict_flags;
/* DESCRIPTION /* DESCRIPTION
/* dict_mysql_open() opens the mysql databases with name dbname on /* dict_mysql_open() creates a dictionary of type 'mysql'. This
/* each host in hostlist and registers under the given name with the /* dictionary is an interface for the postfix key->value mappings
/* dictionary manager. The result is a pointer to the installed dictionary, /* to mysql. The result is a pointer to the installed dictionary,
/* or a null pointer in case of problems. /* 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: /* Arguments:
/* .IP name /* .IP name
/* The path of the MySQL configuration file. The file encodes a number of /* The path of the MySQL configuration file. The file encodes a number of
@@ -70,8 +78,24 @@
#include "argv.h" #include "argv.h"
#include "vstring.h" #include "vstring.h"
/* external declarations */
extern int dict_errno; 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 { typedef struct {
char *username; char *username;
char *password; char *password;
@@ -90,114 +114,38 @@ typedef struct {
MYSQL_NAME *name; MYSQL_NAME *name;
} DICT_MYSQL; } 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 *);
static MYSQL_NAME *mysqlname_parse(const char *mysqlcf_path)
{
int i;
char *nameval;
char *hosts;
MYSQL_NAME *name = (MYSQL_NAME *) mymalloc(sizeof(MYSQL_NAME));
ARGV *hosts_argv;
dict_load_file("mysql_options", mysqlcf_path);
/* mysql username lookup */
if ((nameval = (char *) dict_lookup("mysql_options", "user")) == NULL)
name->username = mystrdup("");
else
name->username = mystrdup(nameval);
if (msg_verbose)
msg_info("dict_mysql_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);
/* database name lookup */ /**********************************************************************
if ((nameval = (char *) dict_lookup("mysql_options", "dbname")) == NULL) * public interface dict_mysql_lookup
msg_fatal("%s: mysql options file does not include database name", mysqlcf_path); * find database entry return 0 if no alias found, set dict_errno
else * on errors to DICT_ERRBO_RETRY and set dict_errno to 0 on success
name->dbname = mystrdup(nameval); *********************************************************************/
if (msg_verbose)
msg_info("mysql_name_parse: set database name to '%s'", name->dbname);
/* table lookup */
if ((nameval = (char *) dict_lookup("mysql_options", "table")) == NULL)
msg_fatal("%s: mysql options file does not include table name", mysqlcf_path);
else
name->table = mystrdup(nameval);
if (msg_verbose)
msg_info("mysql_name_parse: set table name to '%s'", name->table);
/* select field lookup */
if ((nameval = (char *) dict_lookup("mysql_options", "select_field")) == NULL)
msg_fatal("%s: mysql options file does not include select field", mysqlcf_path);
else
name->select_field = mystrdup(nameval);
if (msg_verbose)
msg_info("mysql_name_parse: set select_field to '%s'", name->select_field);
/* where field lookup */
if ((nameval = (char *) dict_lookup("mysql_options", "where_field")) == NULL)
msg_fatal("%s: mysql options file does not include where field", mysqlcf_path);
else
name->where_field = mystrdup(nameval);
if (msg_verbose)
msg_info("mysql_name_parse: set where_field to '%s'", name->where_field);
/* additional conditions */
if ((nameval = (char *) dict_lookup("mysql_options", "additional_conditions")) == NULL)
name->additional_conditions = mystrdup("");
else
name->additional_conditions = mystrdup(nameval);
if (msg_verbose)
msg_info("mysql_name_parse: set additional_conditions to '%s'", name->additional_conditions);
/* mysql server hosts */
if ((nameval = (char *) dict_lookup("mysql_options", "hosts")) == NULL)
hosts = mystrdup("");
else
hosts = mystrdup(nameval);
/* coo argv interface */
hosts_argv = argv_split(hosts, " ");
argv_terminate(hosts_argv);
if (hosts_argv->argc == 0) { /* no hosts specified,
* default to 'localhost' */
msg_info("mysql_name_parse: no hostnames specified, defaulting to 'localhost'");
name->len_hosts = 1;
name->hostnames = (char **) mymalloc(sizeof(char *));
name->hostnames[0] = mystrdup("localhost");
} else {
name->len_hosts = hosts_argv->argc;
name->hostnames = (char **) mymalloc((sizeof(char *)) * name->len_hosts);
i = 0;
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]);
}
}
myfree(hosts);
argv_free(hosts_argv);
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) static const char *dict_mysql_lookup(DICT *dict, const char *name)
{ {
MYSQL_RES *query_res; MYSQL_RES *query_res;
MYSQL_ROW row; MYSQL_ROW row;
int i,
numrows;
static VSTRING *result;
static VSTRING *query = 0;
char *name_escaped = 0;
DICT_MYSQL *dict_mysql; DICT_MYSQL *dict_mysql;
PLMYSQL *pldb; PLMYSQL *pldb;
static VSTRING *result;
static VSTRING *query = 0;
int i,
numrows;
char *name_escaped = 0;
dict_mysql = (DICT_MYSQL *) dict; dict_mysql = (DICT_MYSQL *) dict;
pldb = dict_mysql->pldb; pldb = dict_mysql->pldb;
@@ -216,8 +164,12 @@ static const char *dict_mysql_lookup(DICT *dict, const char *name)
msg_info("dict_mysql_lookup using sql query: %s", vstring_str(query)); msg_info("dict_mysql_lookup using sql query: %s", vstring_str(query));
/* free mem associated with preparing the query */ /* free mem associated with preparing the query */
myfree(name_escaped); myfree(name_escaped);
/* do the query */ /* do the query - set dict_errno & cleanup if there's an error */
if ((query_res = plmysql_query(pldb, vstring_str(query))) == NULL) { 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; dict_errno = DICT_ERR_RETRY;
vstring_free(query); vstring_free(query);
return 0; return 0;
@@ -247,7 +199,303 @@ static const char *dict_mysql_lookup(DICT *dict, const char *name)
return vstring_str(result); return vstring_str(result);
} }
/* dict_mysql_close - unregister, disassociate from database */ /*
* 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;
char *nameval;
char *hosts;
MYSQL_NAME *name = (MYSQL_NAME *) mymalloc(sizeof(MYSQL_NAME));
ARGV *hosts_argv;
dict_load_file("mysql_options", mysqlcf_path);
/* mysql username lookup */
if ((nameval = (char *) dict_lookup("mysql_options", "user")) == NULL)
name->username = mystrdup("");
else
name->username = mystrdup(nameval);
if (msg_verbose)
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("mysqlname_parse(): set password to '%s'", name->password);
/* database name lookup */
if ((nameval = (char *) dict_lookup("mysql_options", "dbname")) == NULL)
msg_fatal("%s: mysql options file does not include database name", mysqlcf_path);
else
name->dbname = mystrdup(nameval);
if (msg_verbose)
msg_info("mysqlname_parse(): set database name to '%s'", name->dbname);
/* table lookup */
if ((nameval = (char *) dict_lookup("mysql_options", "table")) == NULL)
msg_fatal("%s: mysql options file does not include table name", mysqlcf_path);
else
name->table = mystrdup(nameval);
if (msg_verbose)
msg_info("mysqlname_parse(): set table name to '%s'", name->table);
/* select field lookup */
if ((nameval = (char *) dict_lookup("mysql_options", "select_field")) == NULL)
msg_fatal("%s: mysql options file does not include select field", mysqlcf_path);
else
name->select_field = mystrdup(nameval);
if (msg_verbose)
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)
msg_fatal("%s: mysql options file does not include where field", mysqlcf_path);
else
name->where_field = mystrdup(nameval);
if (msg_verbose)
msg_info("mysqlname_parse(): set where_field to '%s'", name->where_field);
/* additional conditions */
if ((nameval = (char *) dict_lookup("mysql_options", "additional_conditions")) == NULL)
name->additional_conditions = mystrdup("");
else
name->additional_conditions = mystrdup(nameval);
if (msg_verbose)
msg_info("mysqlname_parse(): set additional_conditions to '%s'", name->additional_conditions);
/* mysql server hosts */
if ((nameval = (char *) dict_lookup("mysql_options", "hosts")) == NULL)
hosts = mystrdup("");
else
hosts = mystrdup(nameval);
/* coo argv interface */
hosts_argv = argv_split(hosts, " ");
argv_terminate(hosts_argv);
if (hosts_argv->argc == 0) { /* no hosts specified,
* default 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");
} else {
name->len_hosts = hosts_argv->argc;
name->hostnames = (char **) mymalloc((sizeof(char *)) * name->len_hosts);
i = 0;
for (i = 0; hosts_argv->argv[i] != NULL; i++) {
name->hostnames[i] = mystrdup(hosts_argv->argv[i]);
if (msg_verbose)
msg_info("mysqlname_parse(): adding host '%s' to list of mysql server hosts",
name->hostnames[i]);
}
}
myfree(hosts);
argv_free(hosts_argv);
return name;
}
/*
* 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;
HOST host;
if ((PLDB = (PLMYSQL *) mymalloc(sizeof(PLMYSQL))) == NULL) {
msg_fatal("mymalloc of pldb failed");
}
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;
}
/* host_init - initialize HOST structure */
static HOST host_init(char *hostname)
{
int stat;
MYSQL db;
time_t ts;
HOST host;
host.stat = STATUNTRIED;
host.hostname = hostname;
host.db = db;
host.ts = ts;
return host;
}
/**********************************************************************
* public interface dict_mysql_close
* unregister, disassociate from database, freeing appropriate memory
**********************************************************************/
static void dict_mysql_close(DICT *dict) static void dict_mysql_close(DICT *dict)
{ {
int i; int i;
@@ -267,87 +515,8 @@ static void dict_mysql_close(DICT *dict)
myfree((char *) dict_mysql->name); 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");
}
/* 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)
{
int stat;
MYSQL db;
time_t ts;
HOST host;
host.stat = STATUNTRIED;
host.hostname = hostname;
host.db = db;
host.ts = ts;
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)
{
PLMYSQL *PLDB;
MYSQL *dbs;
int i;
HOST host;
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]);
}
return PLDB;
}
/* plmysql_dealloc - free memory associated with PLMYSQL close databases */ /* plmysql_dealloc - free memory associated with PLMYSQL close databases */
void plmysql_dealloc(PLMYSQL *PLDB) static void plmysql_dealloc(PLMYSQL *PLDB)
{ {
int i; int i;
@@ -359,96 +528,16 @@ void plmysql_dealloc(PLMYSQL *PLDB)
myfree((char *) (PLDB)); myfree((char *) (PLDB));
} }
/* plmysql_down_host - down a HOST * */
inline void plmysql_down_host(HOST *host) /**********************************************************************
* 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)
{ {
if (host->stat != STATFAIL) DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict;
host->ts = time(&(host->ts));
host->stat = STATFAIL;
}
/* plmysql_connect_single - msg_fatal("dict_mysql_update: attempt to update mysql database");
* 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)
{
int i;
MYSQL_RES *res;
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;
} }
#endif #endif

View File

@@ -6,41 +6,9 @@
#define STATACTIVE 0 #define STATACTIVE 0
#define STATFAIL 1 #define STATFAIL 1
#define STATUNTRIED 2 #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); 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 #endif