# -*- perl -*-

# Copyright (c) 2002 by Jeff Weisberg
# Author: Jeff Weisberg <argus @ tcp4me.com>
# Date: 2002-Oct-31 13:37 (EST)
# Function: client side of graph data collector
#
# $Id: Graph.pm,v 1.6 2004/02/05 16:16:04 jaw Exp $

# send data samples to a graphing daemon

package Graph;
@ISA = qw(BaseIO);

use Socket;
use POSIX ('_exit');

use strict qw(refs vars);
use vars qw($doc @ISA);

my $GRAPH_TO = 120;
my $graph;

$doc = {
    package => __PACKAGE__,
    file    => __FILE__,
    isa     => [@ISA],
    methods => {
    },
    fields => {
      graph::wbuffer => {},
      graph::closeme => {},
      graph::pid     => {},
    },
};

sub new {
    my $me = {};
    my( $fh, $pid );
    
    bless $me;
    $me->{type} = 'Graph';
    $me->debug( "new" );
    $me->{fd} = $fh = BaseIO::anon_fh();

    my $prog = $Conf::Config{graphd_prog} || "$::libdir/graphd";
    
    # we need to know if the child dies, pipe won't tell us (but we can catch sigchld)
    # but socketpair will select as readable if the child dies
    unless( socketpair($fh, GR, AF_UNIX, SOCK_STREAM, PF_UNSPEC) ){
	my $m = "pipe failed: $!";
	::sysproblem( "GRAPH $m" );
	$me->debug( $m );
	$me->done();
	return;

    }
    $me->baseio_init();

    unless( ($pid = fork) ){
	# child
        BaseIO::closeall();
	close STDIN;  open( STDIN, "<&GR" );
	close STDOUT; open( STDOUT, ">/dev/null" );
	# let stderr go where ever argus's stderr goes
	close $fh;
	close GR;
	exec( "$prog $Conf::Config{syslog}" );
	# no, I didn't mean system() when I said exec()
	# and yes, I am aware the following statement is unlikely to be reached.
	_exit(-1);
    }
    close GR;

    if( !defined($pid) ){
	# fork failed
	my $m = "fork failed: $!";
	::sysproblem( "GRAPH $m" );
	$me->debug( $m );
	return $me->done();
    }

    $me->{graph}{pid} = $pid;
    # need to learn if child dies, ask Prog for help
    # (just in case we don't already know)
    Prog::register( $me, $pid );
    $me->wantread(1);
    $me->wantwrit(0);
    $me->settimeout(0);
    $graph = $me; 
}


sub write {
    my $msg = shift;

    unless( $graph ){
	new() || return;
    }
    
    $graph->{graph}{wbuffer} .= $msg;
    $graph->wantwrit(1);
    $graph->settimeout($GRAPH_TO);
}

sub writable {
    my $me = shift;
    my $fh = $me->{fd};
    
    if( $me->{graph}{wbuffer} ){
	my( $i, $l );
	
	$l = length($me->{graph}{wbuffer});
	$i = syswrite $fh, $me->{graph}{wbuffer}, $l;
	if( ! $i ){
	    $me->debug( "write failed: $!" );
	    $me->done();
	    return;
	}else{
	    $me->debug( "wrote $i bytes" );
	    $me->{graph}{wbuffer} = substr($me->{graph}{wbuffer}, $i, $l );
	}
    }

    if( $me->{graph}{wbuffer} ){
	$me->settimeout($GRAPH_TO);
	$me->wantwrit(1);
    }else{
	# Stop close their mouths, let them not speak a word.
	#   -- Shakespeare, Titus Andronicus
	$me->settimeout(0);
	$me->wantwrit(0);
	if( $me->{graph}{closeme} ){
	    $me->done();
	}
    }
}

sub readable {
    my $me = shift;

    $me->debug( "readable" );
    $me->wantread(0);
    $me->done();
}

sub timeout {
    my $me = shift;

    $me->debug( "TO" );
    $me->done();
}

sub progdone {
    my $me = shift;

    $me->debug( "reaped" );
    $me->done();
}

# Done to death by slanderous tongues
#   -- Shakespeare, Much Ado about Nothing
sub done {
    my $me = shift;
    
    $me->shutdown();
    $graph = undef if $graph == $me;
}

sub debug {
    my $me  = shift;
    my $msg = shift;

    # print STDERR "GRAPH DEBUG: $msg\n";
}

################################################################

sub MonEl::graph_add_sample {
    my $me  = shift;
    my $val = shift;
    my $st  = shift;

    $val = $me->{srvc}{elapsed}
    	if $me->{image}{gr_what} eq 'elapsed';
    $val = $st eq 'up' ? 1 : 0
	if $me->{image}{gr_what} eq 'status';
    
    $val = 0 unless defined $val;
    
    # make numeric
    if( $val + 0 ){
	$val += 0;
    }else{
	$val =~ s/^.*(-?\d+\.?\d*).*/$1/;
    }

    # NB: normal graphd does not use tag in any way
    # provided for interesting add-ons
    my $tag;
    $tag = $me->graph_tag() if $me->can('graph_tag');
    $tag ||= '';
    
    Graph::write( "sample $^T " . $me->filename() . " $st $val $tag\n" );
}

1;

