#!/usr/bin/perl -wT
# -*- perl -*-

# Copyright (C) 2004-2009
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; version 2 dated June,
# 1991.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# $Id: munin-run 3461 2010-03-25 11:35:29Z janl $
#

use strict;
use warnings;

# Trust PERL5LIB from environment
use lib map { /(.*)/ } split(/:/, ($ENV{PERL5LIB} || ''));

use Carp;
use Getopt::Long;

use Munin::Common::Defaults;
use Munin::Node::Config;
use Munin::Node::OS;
use Munin::Node::Service;

my $FQDN = "";

my $defuser    = getpwnam($Munin::Common::Defaults::MUNIN_PLUGINUSER);
my $defgroup   = getgrnam($Munin::Common::Defaults::MUNIN_GROUP);

my $paranoia = 0;

my $config = Munin::Node::Config->instance();


sub main
{
    # "Clean" environment to disable taint-checking on the environment. We _know_
    # that the environment is insecure, but we want to let admins shoot themselves
    # in the foot with it, if they want to.
    foreach my $key (keys %ENV) {
        $ENV{$key} =~ /^(.*)$/;
        $ENV{$key} = $1;
    }

    $0 =~ /^(.*)$/;
    $0 = $1;

    my ($plugin, $arg) = parse_args();

    # Loads the settings from munin-node.conf.
    # Ensures that, where options can be set both in the config and in
    # @ARGV, the latter takes precedence.
    $paranoia = $config->{paranoia};

    die "Fatal error. Bailing out.\n"
        unless Munin::Node::OS->check_perms_if_paranoid($config->{conffile});
    $config->parse_config_from_file($config->{conffile});

    die "Fatal error. Bailing out.\n"
        unless (Munin::Node::OS->check_perms_if_paranoid($config->{servicedir}));

    $defuser  = $config->{defuser}  if defined $config->{defuser};
    $defgroup = $config->{defgroup} if defined $config->{defgroup};

    $config->reinitialize({
        %$config,

        defuser    => $defuser,
        defgroup   => $defgroup,
        paranoia   => $paranoia,
    });

    unless (Munin::Node::Service->is_a_runnable_service($plugin)) {
        print STDERR "# Unknown service '$plugin'\n";
        exit 1;
    }

    Munin::Node::Service->prepare_plugin_environment($plugin);

    # no need for a timeout -- the user can kill this process any
    # time they want.
    Munin::Node::Service->exec_service($config->{servicedir}, $plugin, $arg);

    # Never reached, but just in case...
    print STDERR "# FATAL: Failed to exec.\n";
    exit 42;
}


sub parse_args
{
    # Default configuration values
    my $servicedir = "$Munin::Common::Defaults::MUNIN_CONFDIR/plugins";
    my $sconfdir   = "$Munin::Common::Defaults::MUNIN_CONFDIR/plugin-conf.d";
    my $conffile   = "$Munin::Common::Defaults::MUNIN_CONFDIR/munin-node.conf";
    my $sconffile;

    my ($DEBUG, $PIDEBUG) = (0, 0);
    my ($plugin, $arg);

    print_usage_and_exit() unless GetOptions (
            "config=s"     => \$conffile,
            "debug!"       => \$DEBUG,
            "pidebug!"     => \$PIDEBUG,
            "version!"     => \&print_version_and_exit,
            "servicedir=s" => \$servicedir,
            "sconfdir=s"   => \$sconfdir,
            "sconffile=s"  => \$sconffile,
            "paranoia!"    => \$paranoia,
            "help"         => \&print_usage_and_exit
    );

    print_usage_and_exit() unless $ARGV[0];

    # Detaint the plugin name
    if ($ARGV[0] =~ m/^([-\w.:]+)$/) {
        $plugin = $1;
    }
    else {
        die "# ERROR: Invalid plugin name.\n";
    }

    if ($ARGV[1]) {
        # Ensure the plugin's argument (if any) is one of the known ones.
        # Incidentally also makes it taint-safe.
        ($arg) = grep { $_ eq $ARGV[1] } qw/config autoconf snmpconf suggest/;
        warn "# Warning: Unrecognised argument '$ARGV[1]' passed to plugin.\n"
            unless $arg;
    }

    # Detaint service dir. #FIX do more strict detainting?
    $servicedir =~ /(.*)/;
    $servicedir = $1;

    # Update the config
    $config->reinitialize({
        %$config,

        servicedir => $servicedir,
        sconfdir   => $sconfdir,
        conffile   => $conffile,
        sconffile  => $sconffile,
        DEBUG      => $DEBUG,
        PIDEBUG    => $PIDEBUG,
        paranoia   => $paranoia,
    });

    return ($plugin, $arg);
}


sub print_version_and_exit
{
    print qq{munin-run (munin-node) version $Munin::Common::Defaults::MUNIN_VERSION.
Written by Audun Ytterdal, Jimmy Olsen, Tore Anderson / Linpro AS

Copyright (C) 2002-2009

This is free software released under the GNU General Public License. There
is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. For details, please refer to the file COPYING that is included
with this software or refer to
  http://www.fsf.org/licensing/licenses/gpl.txt
};

    exit 0;
}


sub print_usage_and_exit
{
    print qq{Usage: $0 [options] <plugin> [ config | autoconf | snmpconf | suggest ]

Options:
    --help              View this message.
    --config <file>     Use <file> as configuration file.
                        [$Munin::Common::Defaults::MUNIN_CONFDIR/munin-node.conf]
    --servicedir <dir>  Dir where plugins are found.
                        [$Munin::Common::Defaults::MUNIN_CONFDIR/plugins]
    --sconfdir <dir>    Dir where plugin configurations are found.
                        [$Munin::Common::Defaults::MUNIN_CONFDIR/plugin-conf.d]
    --sconffile <file>  Use <file> as plugin configuration. Overrides sconfdir.
                        [undefined]
    --[no]paranoia      Only run plugins owned by root. Check permissions.
                        [--noparanoia]
    --debug             View debug messages
    --pidebug           Plugin debug.  Sets the environment variable
                        MUNIN_DEBUG to 1 so that plugins may enable debugging.
    --version           View version information.
};

    exit 0;
}


exit main() unless caller;


1;

__END__

=head1 NAME

munin-run - A program to run Munin plugins from the command line

=head1 SYNOPSIS

munin-run [options] <plugin> [ config | autoconf | snmpconf | suggest ]

=head1 OPTIONS

=over 5

=item B<< --config <configfile> >>

Use E<lt>fileE<gt> as configuration file. [@@CONFDIR@@/munin-node.conf]

=item B<< --servicedir <dir> >>

Use E<lt>dirE<gt> as plugin dir. [@@CONFDIR@@/plugins/]

=item B<< --sconfdir <dir> >>

Use E<lt>dirE<gt> as plugin configuration dir. [@@CONFDIR@@/plugin-conf.d/]

=item B<< --sconffile <file> >>

Use E<lt>fileE<gt> as plugin configuration. Overrides sconfdir.  [undefined]

=item B<--paranoia >

Only run plugins owned by root and check permissions.  [disabled]

=item B<--help >

View this help message.

=item B<--debug >

Print debug messages.  Debug messages are sent to STDOUT and are
prefixed with "#" (this makes it easier for other parts of munin to
use munin-run and still have --debug on).  Only errors go to STDERR.

=item B<--pidebug >

Plugin debug.  Sets the environment variable MUNIN_DEBUG to 1 so
that plugins may enable debugging.  [disabled]

=item B<--version >

Show version information.

=back

=head1 DESCRIPTION

munin-run is a script to run Munin plugins from the command-line.
It's useful when debugging plugins, as they are run in the same conditions
as they are under munin-node.

=head1 FILES

    @@CONFDIR@@/munin-node.conf
    @@CONFDIR@@/plugins/*
    @@CONFDIR@@/plugin-conf.d/*
    @@STATEDIR@@/munin-node.pid
    @@LOGDIR@@/munin-node.log

=head1 VERSION

This is munin-node v@@VERSION@@

=head1 AUTHORS

Audun Ytterdal, Jimmy Olsen, Tore Anderson, Nicolai Langfeldt.

=head1 BUGS

Please see L<http://munin.projects.linpro.no/report/1>.

=head1 COPYRIGHT

Copyright (C) 2002-2009 Audun Ytterdal, Jimmy Olsen, Tore Anderson,
Nicolai Langfeldt / Linpro AS.

This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.

This program is released under the GNU General Public License

=cut

# vim:syntax=perl : ts=4 : expandtab
