(Working) support for multiple plugin directories, with a fix from Nick

Leverton <nj|@|leverton.org>.  The inner _load_plugins() routine is changed to
load only a single plugin given a search path, and the (two) calls to it pass
in the configured list of plugin dirs.  The non-module case of _load_plugin()
simply loops on the plugin dir list until a matching plugin file is found;
the first match stops the search for that plugin, regardless of success or
failure in loading it.


git-svn-id: https://svn.perl.org/qpsmtpd/branches/0.3x@671 958fd67b-6ff1-0310-b445-bb7760255be9
This commit is contained in:
Devin Carraway 2006-11-05 09:54:03 +00:00
parent 02bf7b80e5
commit af5f025b51
2 changed files with 76 additions and 58 deletions

View File

@ -1,3 +1,7 @@
0.3x
Add support for multiple plugin directories, whose paths are given by the
'plugin_dirs' configuration. (Devin Carraway, Nick Leverton)
0.33 0.33
New Qpsmtpd::Postfix::Constants to encapsulate all of the current return New Qpsmtpd::Postfix::Constants to encapsulate all of the current return
codes from Postfix, plus script to generate it. (Hanno Hecker) codes from Postfix, plus script to generate it. (Hanno Hecker)

View File

@ -19,11 +19,17 @@ sub load_logging {
my $configdir = $self->config_dir("logging"); my $configdir = $self->config_dir("logging");
my $configfile = "$configdir/logging"; my $configfile = "$configdir/logging";
my @loggers = $self->_config_from_file($configfile,'logging'); my @loggers = $self->_config_from_file($configfile,'logging');
my $dir = $self->plugin_dir;
$self->_load_plugins($dir, @loggers); $configdir = $self->config_dir('plugin_dirs');
$configfile = "$configdir/plugin_dirs";
my @plugin_dirs = $self->_config_from_file($configfile,'plugin_dirs');
foreach my $logger (@loggers) { my @loaded;
for my $logger (@loggers) {
push @loaded, $self->_load_plugin($logger, @plugin_dirs);
}
foreach my $logger (@loaded) {
$self->log(LOGINFO, "Loaded $logger"); $self->log(LOGINFO, "Loaded $logger");
} }
@ -121,9 +127,15 @@ sub config_dir {
return $configdir; return $configdir;
} }
sub plugin_dir { sub plugin_dirs {
my ($name) = ($0 =~ m!(.*?)/([^/]+)$!); my $self = shift;
my $dir = "$name/plugins"; my @plugin_dirs = $self->config('plugin_dirs');
unless (@plugin_dirs) {
my ($name) = ($0 =~ m!(.*?)/([^/]+)$!);
@plugin_dirs = ( "$name/plugins" );
}
return @plugin_dirs;
} }
sub get_qmail_config { sub get_qmail_config {
@ -244,71 +256,73 @@ sub load_plugins {
$self->{hooks} = {}; $self->{hooks} = {};
my @plugins = $self->config('plugins'); my @plugins = $self->config('plugins');
my @loaded;
my $dir = $self->plugin_dir; for my $plugin_line (@plugins) {
$self->log(LOGNOTICE, "loading plugins from $dir"); push @loaded, $self->_load_plugin($plugin_line, $self->plugin_dirs);
}
@plugins = $self->_load_plugins($dir, @plugins); return @loaded;
return @plugins;
} }
sub _load_plugins { sub _load_plugin {
my $self = shift; my $self = shift;
my ($dir, @plugins) = @_; my ($plugin_line, @plugin_dirs) = @_;
my @ret; my @ret;
for my $plugin_line (@plugins) { my ($plugin, @args) = split ' ', $plugin_line;
my ($plugin, @args) = split ' ', $plugin_line;
my $package; my $package;
if ($plugin =~ m/::/) { if ($plugin =~ m/::/) {
# "full" package plugin (My::Plugin) # "full" package plugin (My::Plugin)
$package = $plugin; $package = $plugin;
$package =~ s/[^_a-z0-9:]+//gi; $package =~ s/[^_a-z0-9:]+//gi;
my $eval = qq[require $package;\n] my $eval = qq[require $package;\n]
.qq[sub ${plugin}::plugin_name { '$plugin' }]; .qq[sub ${plugin}::plugin_name { '$plugin' }];
$eval =~ m/(.*)/s; $eval =~ m/(.*)/s;
$eval = $1; $eval = $1;
eval $eval; eval $eval;
die "Failed loading $package - eval $@" if $@; die "Failed loading $package - eval $@" if $@;
$self->log(LOGDEBUG, "Loading $package ($plugin_line)") $self->log(LOGDEBUG, "Loading $package ($plugin_line)")
unless $plugin_line =~ /logging/; unless $plugin_line =~ /logging/;
} }
else { else {
# regular plugins/$plugin plugin # regular plugins/$plugin plugin
my $plugin_name = $plugin; my $plugin_name = $plugin;
$plugin =~ s/:\d+$//; # after this point, only used for filename $plugin =~ s/:\d+$//; # after this point, only used for filename
# Escape everything into valid perl identifiers # Escape everything into valid perl identifiers
$plugin_name =~ s/([^A-Za-z0-9_\/])/sprintf("_%2x",unpack("C",$1))/eg; $plugin_name =~ s/([^A-Za-z0-9_\/])/sprintf("_%2x",unpack("C",$1))/eg;
# second pass cares for slashes and words starting with a digit # second pass cares for slashes and words starting with a digit
$plugin_name =~ s{ $plugin_name =~ s{
(/+) # directory (/+) # directory
(\d?) # package's first character (\d?) # package's first character
}[ }[
"::" . (length $2 ? sprintf("_%2x",unpack("C",$2)) : "") "::" . (length $2 ? sprintf("_%2x",unpack("C",$2)) : "")
]egx; ]egx;
$package = "Qpsmtpd::Plugin::$plugin_name"; $package = "Qpsmtpd::Plugin::$plugin_name";
# don't reload plugins if they are already loaded # don't reload plugins if they are already loaded
unless ( defined &{"${package}::plugin_name"} ) { unless ( defined &{"${package}::plugin_name"} ) {
Qpsmtpd::Plugin->compile($plugin_name, PLUGIN_DIR: for my $dir (@plugin_dirs) {
$package, "$dir/$plugin", $self->{_test_mode}); if (-e "$dir/$plugin") {
$self->log(LOGDEBUG, "Loading $plugin_line") Qpsmtpd::Plugin->compile($plugin_name, $package,
unless $plugin_line =~ /logging/; "$dir/$plugin", $self->{_test_mode});
$self->log(LOGDEBUG, "Loading $plugin_line from $dir/$plugin")
unless $plugin_line =~ /logging/;
last PLUGIN_DIR;
}
} }
} }
my $plug = $package->new();
push @ret, $plug;
$plug->_register($self, @args);
} }
my $plug = $package->new();
$plug->_register($self, @args);
push @ret, $plug;
return @ret; return @ret;
} }