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

Reply via email to