2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-31 06:05:37 +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
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

View 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.

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
@@ -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

View 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

View 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/

View 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

View File

@@ -17,6 +17,14 @@
/* 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);
}

View 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

View 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

View 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>

View 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)

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.
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>

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,
* 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 */
}
/*

View 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);

View 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

View 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

View 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
@@ -19,12 +19,20 @@
/* 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;

View File

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

View 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
@@ -38,12 +38,20 @@
/* 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;

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.
*/
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)

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
* 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));

View 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);

View File

@@ -12,11 +12,19 @@
/* 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
@@ -70,8 +78,24 @@
#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,114 +114,38 @@ 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 *);
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)
msg_fatal("%s: mysql options file does not include database name", mysqlcf_path);
else
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 */
/**********************************************************************
* 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;
int i,
numrows;
static VSTRING *result;
static VSTRING *query = 0;
char *name_escaped = 0;
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;
@@ -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));
/* free mem associated with preparing the query */
myfree(name_escaped);
/* do the query */
if ((query_res = plmysql_query(pldb, vstring_str(query))) == NULL) {
/* 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;
@@ -247,7 +199,303 @@ static const char *dict_mysql_lookup(DICT *dict, const char *name)
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)
{
int i;
@@ -267,87 +515,8 @@ static void dict_mysql_close(DICT *dict)
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 */
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)
/**********************************************************************
* 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)
host->ts = time(&(host->ts));
host->stat = STATFAIL;
}
DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict;
/* 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)
{
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;
msg_fatal("dict_mysql_update: attempt to update mysql database");
}
#endif

View 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