On Wed, 21 Feb 2007 23:24:29 +0100
Michael Holzt <[EMAIL PROTECTED]> wrote:
> > Is there interest?
>
> Absolutely.
Here it is as two attachments. One attachment is a patch to
trunk/lib/Qpsmtpd.pm. The other is a file explaining how to use
the new feature.
What do you think of it conceptually? Is there anything you'd like me to
change before it could be accepted into trunk?
I anticipate a suggestion to turn it into a plugin, but the config hook is
designed for replacing the config storage, not the naming scheme. Is it
even possible to do this as a plugin?
One caveat: While this does work great in my high-perf-based branch, I
haven't actually tested this in trunk.
Brian
=head1 NAME
PERHOST config files
=head1 CF
The normal api for config is documented ... in the source.
=head1 MOTIVATION
Ignore this feature unless you run multiple copies of qpsmtpd with
different configurations, say for example one for MX and one for submission
service, and maybe one as a honeypot. Or one for each of several clients.
Or a different IP for each host.
For example, I keep qpsmtpd under svn with the configs for over twenty
distinct qpsmtpd instances.
=head1 DESCRIPTION
The traditional configuration directory is used as a fallback. If the config
file isn't found in the config/PERHOST hierarchy, the file (if any) found in
the usual place is used.
The most specific config file is used. The first "file" from this list is used:
config/PERHOST/ids/$ID/file
config/PERHOST/$CLIENT/$ROLE/file
config/PERHOST/$CLIENT/file
config/PERHOST/$ROLE/file
config/file
You specify $ID, $CLIENT and $ROLE by first puting the $ID value in
config/PERHOST/id. $CLIENT is then in config/PERHOST/ids/$ID/client; and
$ROLE in config/PERHOST/ids/$ID/role.
I recommend creating the config/PERHOST/id in your run script each time you
start that qpsmtpd.
=head1 SUMMARY
config/ base configs
config/PERHOST/ put id and ids/ here
config/PERHOST/id $ID (set this for each install -- do NOT put
in svn/cvs/arch!)
(eg. "external/testhost")
config/PERHOST/ids/ configs per instance id
config/PERHOST/ids/$ID/ per-id config files
config/PERHOST/ids/$ID/client $CLIENT (eg. "internal" or "external; or
"ourself",
"customer1", "customer2")
config/PERHOST/ids/$ID/role $ROLE (eg. "mx" or "submission" or "honeypot")
config/PERHOST/$CLIENT/ per-client config files
config/PERHOST/$ROLE/ per-role config files
config/PERHOST/$CLIENT/$ROLE/ per-client/role config files
=head1 EXAMPLES
eg. 1 (simple) - note how client and role default to "."
config/PERHOST/id "testhost"
config/PERHOST/././plugins our default plugins file
config/PERHOST/ids/testhost/plugins we may use different plugins
per $ID
eg. 2
config/PERHOST/id "ourselves/testhost"
config/PERHOST/ids/ourselves/testhost/client "ourown"
config/PERHOST/ids/ourselves/testhost/role "mx"
config/PERHOST/ourown/signatures_exe our custom executable
signatures list;
applies to all roles
unless overridden
config/PERHOST/ourown/mx/plugins of course we use
different plugins per role
config/PERHOST/ids/ourselves/testhost/IP duh :)
config/PERHOST/ids/ourselves/testhost/plugins we may use different
plugins per $ID
--- trunk/lib/Qpsmtpd.pm-orig 2007-02-21 15:10:18.000000000 -0700
+++ trunk/lib/Qpsmtpd.pm 2007-02-22 03:28:41.000000000 -0700
@@ -119,7 +119,7 @@
}
}
-sub config_dir {
+sub config_dir_original {
my ($self, $config) = @_;
my $configdir = ($ENV{QMAIL} || '/var/qmail') . '/control';
my ($path) = ($ENV{PROCESS} ? $ENV{PROCESS} : $0) =~ m!(.*?)/([^/]+)$!;
@@ -128,9 +128,39 @@
$ENV{QPSMTPD_CONFIG} =~ /^(.*)$/; # detaint
$configdir = $1 if -e "$1/$config";
}
+ return undef if $config eq 'PERHOST' && ! -e "$configdir/$config";
return $configdir;
}
+sub config_dir {
+ my ($self, $config) = @_;
+ my $basedir = $self->config_dir_original('PERHOST');
+ if( !defined $basedir ) { # no PERHOST directory, so skip PERHOST support
+ return $self->config_dir_original($config);
+ }
+ my $id = $self->_perhost_config_from_file("$basedir/PERHOST/id",
'perhost/id');
+ my $client =
$self->_perhost_config_from_file("$basedir/PERHOST/ids/$id/client",
'perhost/client');
+ $client = '.' if !defined $client;
+ my $role = $self->_perhost_config_from_file("$basedir/PERHOST/ids/$id/role",
'perhost/role');
+ $role = '.' if !defined $role;
+ my $configdir = $basedir; # fallback, like &config_dir_original uses
/var/qmail
+ $configdir = "$basedir/PERHOST/$role" if (-e
"$basedir/PERHOST/$role/$config");
+ $configdir = "$basedir/PERHOST/$client" if (-e
"$basedir/PERHOST/$client/$config");
+ $configdir = "$basedir/PERHOST/$client/$role" if (-e
"$basedir/PERHOST/$client/$role/$config");
+ $configdir = "$basedir/PERHOST/ids/$id" if (-e
"$basedir/PERHOST/ids/$id/$config");
+ return $configdir;
+ }
+
+sub _perhost_config_from_file {
+ # special version for efficiently bootstraping id,client,role
+ my ($self, $configfile, $config) = @_;
+ if( exists $self->{_config_cache}->{$config} ) {
+ my $C = $self->{_config_cache}->{$config};
+ return wantarray ? @$C : $C->[0];
+ }
+ return _config_from_file(@_);
+}
+
sub plugin_dirs {
my $self = shift;
my @plugin_dirs = $self->config('plugin_dirs');