1package Munin::Node::Configure::PluginList; 2 3use strict; 4use warnings; 5 6use File::Basename qw(fileparse); 7 8use Munin::Node::Service; 9use Munin::Node::Configure::Plugin; 10use Munin::Node::Configure::History; 11use Munin::Node::Configure::Debug; 12 13 14sub new 15{ 16 my ($class, %opts) = @_; 17 18 my $libdir = delete $opts{libdir} or die "Must specify the directory\n"; 19 my $servicedir = delete $opts{servicedir} or die "Must specify the service directory\n"; 20 21 my $library = Munin::Node::Service->new(servicedir => $libdir); 22 my $services = Munin::Node::Service->new(servicedir => $servicedir); 23 24 my $families = delete $opts{families} or die "Must provide a list of families to load\n"; 25 my $newer = delete $opts{newer}; 26 27 my %plugin = ( 28 libdir => $libdir, 29 library => $library, 30 31 servicedir => $servicedir, 32 services => $services, 33 34 families => $families, 35 newer => $newer, 36 37 %opts, 38 ); 39 40 return bless \%plugin, $class; 41} 42 43 44### Plugin and service enumeration ############################################# 45 46sub load 47{ 48 my ($self) = @_; 49 $self->_load_available(); 50 $self->_load_installed(); 51 return; 52} 53 54 55sub _load_available 56{ 57 my ($self) = @_; 58 59 my @families = @{$self->{families}}; 60 my %found; 61 my $plugin_count = 0; 62 63 my $history = Munin::Node::Configure::History->new( 64 history_file => "$self->{libdir}/plugins.history", 65 newer => $self->{newer}, 66 ); 67 $history->load; 68 69 DEBUG("Searching '$self->{libdir}' for available plugins."); 70 71 foreach my $item (_valid_files($self->{library})) { 72 my $path = $item->{path}; 73 my $plug = $item->{name}; 74 75 DEBUG("Considering '$path'"); 76 77 my $plugin = Munin::Node::Configure::Plugin->new(name => $plug, path => $path); 78 79 $plugin->read_magic_markers(); 80 81 unless ($plugin->in_family(@families)) { 82 DEBUG("\tFamily '$plugin->{family}' is currently ignored. Skipping."); 83 next; 84 } 85 86 if ($history->too_old($plugin)) { 87 DEBUG("\tPlugin is older than $self->{newer}. Skipping."); 88 next; 89 } 90 91 $found{$plug} = $plugin; 92 $plugin_count++; 93 } 94 95 $self->{plugins} = \%found; 96 DEBUG("$plugin_count plugins available."); 97 98 return; 99} 100 101 102sub _load_installed 103{ 104 my ($self) = @_; 105 my $service_count = 0; # the number of services currently installed. 106 107 DEBUG("Searching '$self->{servicedir}' for installed services."); 108 109 foreach my $item (_valid_files($self->{services})) { 110 my $path = $item->{path}; 111 my $service = $item->{name}; 112 113 my $realfile; 114 # Ignore non-symlinks, and symlinks that point anywhere other 115 # than the plugin library 116 next unless -l $path; 117 unless ($realfile = readlink($path)) { 118 # FIXME: should be a given, since it's tested by is_a_runnable_service() 119 DEBUG("Warning: symlink '$path' is broken."); 120 next; 121 } 122 next unless ($realfile =~ /^$self->{libdir}\//); 123 124 DEBUG("Found '$service'"); 125 126 $realfile = fileparse($realfile); 127 unless ($self->{plugins}{$realfile}) { 128 DEBUG("\tCorresponds to an ignored plugin ($realfile). Skipping."); 129 next; 130 } 131 132 $self->{plugins}{$realfile}->add_instance($service); 133 $service_count++; 134 } 135 136 DEBUG("$service_count services currently installed."); 137 return; 138} 139 140 141sub list 142{ 143 my ($self) = @_; 144 my @plugins; 145 foreach my $plug (sort keys %{$self->{plugins}}) { 146 push @plugins, $self->{plugins}{$plug}; 147 } 148 return @plugins; 149} 150 151 152sub names { return keys %{(shift)->{plugins}} } 153 154 155sub _valid_files 156{ 157 my ($dir) = @_; 158 return map { { path => "$dir->{servicedir}/$_", name => $_ } } $dir->list; 159} 160 161 1621; 163 164__END__ 165 166=head1 NAME 167 168Munin::Node::Configure::PluginList - Loading and listing a collection of plugins 169 170 171=head1 SYNOPSIS 172 173 my $plugins = Munin::Node::Configure::PluginList->new( 174 libdir => '/usr/share/munin/plugins/', 175 servicedir => '/etc/munin/plugins/', 176 ); 177 $plugins->load('auto'); 178 foreach my $plugin ($plugins->list) { 179 # do something to each 'auto' plugin in turn 180 } 181 182 183=head1 SUBROUTINES 184 185=over 186 187=item B<new(%args)> 188 189Constructor. 190 191Required arguments are 'libdir' and 'servicedir', which are the plugin library 192and service directory, respectively. 193 194 195=item B<load(@families)> 196 197Finds all the plugins in 'libdir' that are in any of @families, and any 198instances of these plugins in 'servicedir'. 199 200 201=item B<list()> 202 203Returns a list of Munin::Node::Configure::Plugin objects currently loaded, 204sorted alphabetically by name. 205 206 207=item B<names()> 208 209Returns the names of the currently-loaded plugins. 210 211=back 212 213=cut 214# vim: sw=4 : ts=4 : expandtab 215