#!@@PERL@@ -w
# -*- perl -*-
#

=head1 NAME

ifx_concurrent_sessions_ - Munin wildcard plug-in that connects to
Informix instance and counts number of concurrent sessions from
syssqlstat view of sysmaster of instance.

=head1 APPLICABLE SYSTEMS

Connects over the network to Informix databases.  Uses informix
commands to connect to the databases so these must be present on the
munin-node it runs on.

=head1 CONFIGURATION

As Informix installations can vary wildly the autoconf may fail.
However, it is recommended to let munin-node-configure set the plugin
links by e.g.

   # @@SBINDIR@@/munin-node-configure --families contrib --shell |
     grep concurrent | sh -

Yet, for this plugin to work it is most likely required to grant
select to the user the munin-node runs this plugin as.  This usually
requires to login as user "informix" and execute the following
command:

   $ echo "grant select on syssqlstat to munin as informix" |
     INFORMIXDIR=/path/to/ifx_base INFORMIXSERVER=inst_name dbaccess sysmaster

The value of INFORMIXSERVER usually equals to that of DBSERVERNAME in
the $INFORMIXDIR/etc/onconfig file.

=head1 VERSION

 $Id$

=head1 BUGS

This plugin hangs during autoconf - at least on systems without
informix.  Therefore capabilities magic marker has been removed.

May not autoconfigure correctly as noted above.

=head1 AUTHOR

Contributed by <ralph dot grothe at itdz minus berlin dot de>

=head1 LICENSE

GPLv2

=head1 MAGIC MARKERS

  #%# family=manual

=cut

use strict;
use File::Find;  # In perl 5 since the start

my @infdir;
if (@ARGV && $ARGV[0] =~ /autoconf|suggest/) {
    # Taint mode requires this
    local $ENV{PATH} = '/bin:/usr/bin';
    # First, try lookup in proc table
    # Note, this will only work if oninit was started with full path
    # On Linux we could easily retreive INFORMIXDIR from /proc by
    # tr \\00 \\012 < /proc/$PID/environ | grep INFORMIXDIR
    # Following ps command should work on Linux and Solaris
    my $pscmd = 'ps -p `pgrep -P1 oninit -d,` -o ppid= -o args=';
    # while HP-UX discerns itself once more
    $pscmd = 'UNIX95= ps -C oninit -o ppid= -o args='
    	if ($^O =~ /hp-?ux/i);
    @infdir = map $_->[1],
	grep $_->[0]==1,
	map [m|(\d+)\s+(\S+)/bin|],
	qx($pscmd);

    # Second, try lookup in filesystem Might also fail if we cannot
    # run find as root However, an oninit binary must exist somewhere
    # but for sanity reasons we restrict find to /opt and /usr. Note,
    # if this aborts with Perl Taint mode errors try removing -T in
    # shebang

    unless (@infdir) {
	no warnings; # to hush single usage of File::Find::name warning
	File::Find::find(sub { -f $_ && -r _ && /^oninit\z/s
				 && push @infdir,
				   $File::Find::name =~ m[(\S+)/bin]},
			 qw(/opt /usr));
    }

    if ($ARGV[0] eq 'autoconf') {
	if (@infdir) {
	    print "yes\n";
	    exit 0;
	}
	else {
	    print "no (Could not find oninit on this host)\n";
	    exit 0;
	}
    }

    if ($ARGV[0] eq 'suggest') {
	# Here we need to retreive INFORMIXSERVER
	# Common Informix installations stick to $INFORMIXDIR/etc/onconfig
	foreach my $dir (@infdir) {
	    local *ONCONFIG;
	    my $onconfig = "$dir/etc/onconfig";
	    next unless -f $onconfig && -r _;
	    open ONCONFIG, $onconfig or die "Couldn't open $onconfig\n";
	    my ($infsrv) = grep /^\s*DBSERVERNAME/sm, <ONCONFIG>;
	    close ONCONFIG;

	    # The directory separator must be replaced by some shell
	    # immune unusual character Note, munin-run (as probably
	    # munin-node) only permit "." as remaining valid character
	    $dir =~ tr,/,.,;

	    # Store INFORMIXDIR and INFORMIXSERVER in the symlink's
	    # basename This releives the user from having to export
	    # these via plugins.conf entries in matching instance
	    # sections but leads to ugly link names
	    print "$dir.$1\n" if $infsrv =~ /DBSERVERNAME\s+(\S+)/;
	}
    }
    exit 0;
}

# Retreive vital Informix environment from plugin's name
@ENV{qw(INFORMIXDIR INFORMIXSERVER)} = $0 =~ /[^.]+(.+)\.(.+)/;
$ENV{INFORMIXDIR} =~ tr,.,/,;
$ENV{PATH} = "$ENV{INFORMIXDIR}/bin";

# Plugin's config section
if (@ARGV && $ARGV[0] =~ /config/i) {
	my $config = qq{
	    graph_title Number of Concurrent Sessions of $ENV{INFORMIXSERVER}
	    graph_category Informix
	    graph_vlabel Number
            graph_info Number of concurrent sessions to $ENV{INFORMIXSERVER}. Note that a constant value of -1 indicates missing grantsfor the plugin user (often "munin") on syssqlstat view.
	    sessions.label Sessions
	    sessions.type GAUGE
	};
	$config =~ s/^\s+//gm;
	print $config;
	exit 0;
}

#printf "%s\n%s\n", @ENV{qw(INFORMIXDIR INFORMIXSERVER)};exit;

# Usually redundant double check
#local *SQLHOSTS;
#my $SQLHOSTS = "$ENV{INFORMIXDIR}/etc/sqlhosts";
#open SQLHOSTS, $SQLHOSTS or die "Cannot open $SQLHOSTS\n";
#my ($infpar) = map [(split /\s+/, $_)[0,2]],
#	       grep /$IFX_INSTT/, <SQLHOSTS>;
#close SQLHOSTS;

#$ENV{INFORMIXSERVER} = $infpar->[0];

# Actual SQL statement to count sessions
# Could be easily replaced by any other valid Sysmaster query to extend plugin

my $query = q{select count(*) from syssqlstat where sqs_dbname != '-';};

local *DBACCESS;
open DBACCESS, "echo \"$query\"|dbaccess sysmaster 2>/dev/null|"
    or die "Cannot open reading pipe from dbaccess\n";
my ($sessions) = grep /^\s*\d+\s*$/sm, <DBACCESS>;
close DBACCESS;

# Of course, we need to subtract our own session. Note, that constant
# output of -1 often indicates lack of select grant on syssqlstat view
# to the user the munin-node runs this plugin as (usually munin)

print 'sessions.value ', $sessions ? --$sessions : 'U', "\n";
