#!/usr/bin/perl
#    Big Sister network monitor
#    Copyright (C) 2001  Thomas Aeby
#
#    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; either version 2 of the License, or
#    (at your option) any later version.
#
#    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., 675 Mass Ave, Cambridge, MA 02139, USA.
#

#=============================================================================
#
$BigSister::common::Usage	  = "";
#
#=============================================================================
@BigSister::common::options = ( );

use lib "$ENV{BIGSISTER_CHROOT}/usr/share/bigsister/bin"; use lib "$ENV{BIGSISTER_CHROOT}/usr/share/bigsister/uxmon"; #inslib
use BigSister::common;
proginit();

use strict;

my $bsuser = "bigsister";    #bsuser
my $mode = shift;

my @daemons = qw( uxmon bbd bsmon bstrapd );
my @startdaemons = ();

my @ps;
my @messages = ();

$| = 1;

action( $mode );

if( @messages ) {
    print join( "\n   ", "\nMessages:", @messages );
    print "\n";
}


sub action {
    my( $mode ) = @_;

    if( $mode eq "start" ) {
	if( killem( 0, "already running" ) ) {
	    BigSister::common::log( "warning", "starting Big Sister while some components are already running" );
	    print STDERR "WARNING: some components are already running\n";
	}

	print "Starting Big Sister ... \n";

	if( (-f "$BigSister::common::fs{'adm'}/bb-display.cfg") && (-f "$BigSister::common::fs{'etc'}/bsmon.cfg") ) {
	    run_cmd( "$BigSister::common::fs{'bin'}/bsmon", $bsuser, "Monitor bsmon" );
	}

	if( -f "$BigSister::common::fs{'adm'}/bb-display.cfg") {
	    run_cmd( "$BigSister::common::fs{'bin'}/bbd", $bsuser, "Server bbd" );
	}

	if( -f "$BigSister::common::fs{'adm'}/bstrapd.cfg") {
	    run_cmd( "$BigSister::common::fs{'bin'}/bstrapd", "root", "Trap Monitor bstrapd" );
	}

	if( (-f "$BigSister::common::fs{'adm'}/uxmon-asroot")
	    && ( -s "$BigSister::common::fs{'adm'}/uxmon-asroot" < 10 ) ) {
	    push( @messages, split( "\n", 
"WARNING: the meaning of the uxmon-asroot file has changed, now
    uxmon-asroot (and uxmon-asroot.*) file is a valid uxmon-net
    like file. Tests in uxmon-net (and uxmon-net.*) are always
    executed under the non-priviledged $bsuser account, while
    tests in uxmon-asroot (and uxmon-asroot.*) are executed with
    root priviledges. If your uxmon-asroot serves as a flag to
    tell uxmon to run as root - as its old semantics was - then
    you might want to rename your uxmon-net into uxmon-asroot." ));
    
        }
	opendir( DIR, "$BigSister::common::fs{'adm'}" );
	my @uxmon_nets = grep( /^uxmon-(net|asroot)(|\..*)$/, readdir( DIR ));
	foreach my $cfg ( @uxmon_nets ) {
	    next if( $cfg =~ /(bak|[%~]|dpkg.*|rpm.*)$/ );
	    next unless( -s "$BigSister::common::fs{'adm'}/$cfg" );
	    my $asroot = ($cfg =~ /^uxmon-asroot/);
	    run_cmd( "$BigSister::common::fs{'uxmon'}/uxmon -c '$cfg'", $asroot?"root":$bsuser, ($#uxmon_nets)?"Agent uxmon for $cfg":"Agent uxmon" );
	}
	closedir( DIR );
    }
    elsif( $mode eq "stop" ) {
	print "Stopping Big Sister ...\n";
	killem( "TERM", "stopped" );
    }
    elsif( $mode eq "status" ) {
	unless( killem( 0, "is running" ) ) {
	    print "Big Sister is not running\n";
	}
    }
    elsif( $mode eq "restart" ) {
	for( my $i=0; $i<=1; $i++ ) {
	    my @killprocs = $i ? @daemons : ("uxmon");
	    killem( "TERM", "stopped", @killprocs );
	    @ps = ();
	    sleep 1;
	    my $i = 6;
	    while( (killem( 0, "is still running", @killprocs )) && ($i-- >0) ) {
		killem( "TERM", "stopping again", @killprocs ) if( $i < 2 );
		sleep 1;;
		@ps = ();
	    }
	    @ps = ();
	}
	action( "start" );
    }
}




sub killem {
    my( $signal, $message, @processes ) = @_;
    my $warned = 0;

    @processes = @daemons unless( @processes );
    my $killed = 0;
    unless( @ps ) {
	proclist();
    }
    my $myuid = $>;
    my $bsuid = getpwnam( $bsuser );
    ($bsuid = $myuid) unless( $bsuid );
    foreach my $daemon (@processes) {
	my @pids = grep( /\s$daemon(\s*\(.*\)|)$/ || /.*perl (bin|uxmon)\/$daemon\s/, @ps );
	# change uid to Big Sister user so that we do not kill processes not running
	# under Big Sister account. Do it on a best effort base, if the host system
	# does not support setreuid()/seteuid() we just continue running as root
	my $chuid = ($daemon ne "uxmon") && ($daemon ne "bstrapd");
	eval {
	    if( $chuid ) {
		$> = $bsuid;
	    }
	};
	foreach my $pid (@pids) {
	    $killed++;
	    $pid =~ s/^\s+//;
	    $pid =~ s/\s.*//;
	    unless( $pid =~ /\d+/ ) {
		&BigSister::common::log( "warning", "cannot determine process PIDs" ) unless( $warned );
	        print "$daemon: (process found but no associated pid)\n" if( $message );
		$warned++;
		next;
	    }
	    if( kill( $signal, $pid ) ) {
		print "$daemon: $message\n" if( $message );
	    }
	}
	eval {
	    if( $chuid ) {
		$> = $myuid;
	    }
	};
    }
    return $killed;
}



sub proclist {
    my @result;
    foreach my $cmd ( "ps cax", "ps -e", "ps -ef" ) {
	open( CMD, "$cmd 2>/dev/null|" );
	@result = ();
	while( <CMD> ) {
	    chomp;
	    push( @result, $_ );
	}
	close CMD;
	last unless( $? || ! grep( /(\s|\/)init/, @result ) );
    }
    @ps = @result;
}


sub run_cmd {
    my( $cmd, $user, $name ) = @_;

    my $uid = getpwnam( $user );
    unless( defined $uid ) {
	print STDERR "user $user is unknown - aborting\n";
	BigSister::common::log( "Big Sister start aborted since user $user is unknown" );
	exit 1;
    }
    print sprintf( "  %-50s", $name );
    my $ret;
    chdir( $BigSister::common::root );
    my $pid = open( SUB, "-|" );
    if( $pid == 0 ) {
	close( STDERR );
	open( STDERR, ">&STDOUT" );
	close( STDOUT );
	open( STDOUT, ">/dev/null" );
	if( $uid == $> ) {
	    exec( $cmd );
	}
	else {
	    exec( "su", "-", "$user", "-c", $cmd );
	}
	exit(1);
    }
    while( <SUB> ) {
	chomp;
        push( @messages, "$name: $_" );
    }
    close SUB;
    $ret = $?;
    print ($ret?"FAILED":"OK");
    print "\n";
}







