#!/usr/bin/perl -WT

use strict;
use warnings;

use IO::Socket;
use File::Basename;

$ENV{PATH} = '/usr/sbin:/usr/bin:/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

# The Asterisk Manager TCP/IP API
# http://www.asterisk.org/astdocs/node198.html
# cli> manager show commands

my $self = fileparse($0);
($self) = ($self =~ /([a-z|A-Z|0-9|\.|_]+)/);

my $timeout = 1; # Number of seconds to wait for an Asterisk Manager response
my $DEBUG = 0;


my $sock = new IO::Socket::INET (
	PeerAddr => 'asterisk01',
	PeerPort => '5038',
	Proto => 'tcp'
	) or die( "Could not create socket: $!\n" );


$sock->autoflush(1);


my $actioncount = 0;

open( LOG, ">>/tmp/$self.log" ) or die( "Failed to open logfile $self.log: $!" ) if $DEBUG;

print LOG "*** startup: ",scalar localtime(),"\n" if $DEBUG;

login();

if ( chksiphang() ) {

	print "SIP Hung\n";
	print LOG "SIP Hung\n" if $DEBUG;

	print "Restarting Asterisk\n\n";
	print LOG "Restarting Asterisk\n" if $DEBUG;

#	system( 'asterisk -rx "channel request hangup all"' );  # This hangs and doesn't work...
#	sleep( 1 );
	system( "setsid /etc/rc.d/init.d/asterisk restart >/dev/null 2>&1 &" );

}

logoff();

print LOG "*** end: ",scalar localtime(),"\n" if $DEBUG;
close( LOG ) if $DEBUG;
exit(0);

sub login {

	my $user = '<redacted>';
	my $pass = '<redacted>';

	eval {

		local $SIG{ALRM} = sub { die "alarm\n" };
		alarm( $timeout );

		print $sock "Action: Login\r\n";
		print $sock "Username: $user\r\n";
		print $sock "Secret: $pass\r\n";
		print $sock "\r\n";

		while ( my $line = <$sock> ) { 

			if ( $line =~ /Message: Authentication accepted/ ) {
				last;
			}

		};

		# Turn the alarm off.
		alarm(0);
	};


	# Check to see if we timed out.
	if ( $@ ) {
		die unless $@ eq "alarm\n";   # propagate unexpected errors
	    	print "Command Timed Out\n";
	    	print LOG "Command Timed Out\n" if $DEBUG;

	    	print LOG "Login Failed\n" if $DEBUG;
	    	die( "Login Failed" );

    	}


}


sub chksiphang {

	my $actionid = actionid();

	my $retval = 0;

	print $sock "Action: SIPshowregistry\r\nActionID: $actionid\r\n\r\n";

	my %events = geteventormsg( 'Message: Registrations will follow', 'Event: RegistrationsComplete', $actionid );

	if ( %events ) {

		foreach my $eventno ( sort keys( %events ) ) {

#			if ( $DEBUG ) {
#				foreach my $key ( keys( %{$events{$eventno}} ) ) {
#					print LOG "$eventno) $key: $events{$eventno}{$key} \n";
#				}
#			}

			# Make sure we picked up OUR event
			if ( $events{$eventno}{ActionID} eq $actionid ) {
				if ( $events{$eventno}{State} eq 'Registered' ) {

					# Should be the default of 20 seconds, but it sometimes takes longer to figure it out
 					if ( (time() - $events{$eventno}{RegistrationTime}) > ($events{$eventno}{Refresh} + 120) ) {

						print "$events{$eventno}{Host}\tReg time: $events{$eventno}{RegistrationTime} Time: ", 
							time(), "\tSeconds: ", (time() - $events{$eventno}{RegistrationTime}), "  Refresh: $events{$eventno}{Refresh}\n\n";

						# Need:
						# DONT_OPTIMIZE 
						# DEBUG_THREADS
						# DEBUG_CHANNEL_LOCKS
						#
						print "\n\n";
						print "\n\n----\n\n";
						print `asterisk -rx 'core show locks'`;
						print "\n\n----\n\n";
						print `gdb -ex "thread apply all bt" --batch /usr/sbin/asterisk \$(cat /var/run/asterisk/asterisk.pid)`;
						print "\n\n----\n\n";

						$retval = 1;

						last;

					}

 					if ( (time() - $events{$eventno}{RegistrationTime}) > ($events{$eventno}{Refresh} + 20) ) {
						print "not rebooting:\n  $events{$eventno}{Host}\n   Reg time: $events{$eventno}{RegistrationTime} Time: ", 
							time(), "\n   Seconds: ", (time() - $events{$eventno}{RegistrationTime}), "\n   Refresh: $events{$eventno}{Refresh}\n\n";
					}


				}

			}

		}

	} else {
		print LOG "no events\n" if $DEBUG;
	}

	return $retval;

}


sub logoff {

	print $sock "Action: Logff\r\n";;
	close( $sock );

}


sub geteventormsg {

	# Start getting the text with $starttext up to $endtext, which could be 
	# a blank line.

	my $starttext = shift;
	my $endtext = shift;
	my $actionid = shift;

	my %events;

	my $msg = '';

	eval {

		local $SIG{ALRM} = sub { die "alarm\n" };
		alarm( $timeout );

		my $event = 0;
		my $eventno = 0;
		my $resp = 0;
		while ( my $line = <$sock> ) { 

			# Remove any CR or LF
			$line =~ s/\r|\n//g;
			# Remove any trailing whitespace
			$line =~ s/\s+$//;

			print LOG "${line}\n" if $DEBUG;

			if ( $line eq "$starttext" ) {
				$resp = 1;
			}

			if ( $resp ) {
				$msg .= "${line}";

				if ( $line =~ /^Event:/ and $line !~ /Complete/) {
					$event = 1;
					$eventno++;
					print LOG "** start event ($line)\n" if $DEBUG;
				}

				if ( $event ) {
					if ( $line eq '' ) {
						$event = 0;
						print LOG "** end event ($line)\n" if $DEBUG;
					}

					my ($key) = ($line =~ /(.+):/);
					my ($value) = ($line =~ /.+:\s(.+)/);

					if ( defined $value ) {
						$events{$eventno}{$key} = $value;
					}

				}

				if ( $line eq "$endtext" ) {
					last;
				}
#			} else {
#				print "-- $line";
			}

		};

		# Turn the alarm off.
		alarm(0);
	};


	# Check to see if we timed out.
	if ( $@ ) {
		die unless $@ eq "alarm\n";   # propagate unexpected errors
	    	print "Command Timed Out\n";
	    	print LOG "Command Timed Out\n" if $DEBUG;
    	}


	if ( $endtext ne '' ) {
		return %events;
	} else {
		return $msg;
	}

}


sub actionid {

	$actioncount++;
	return  "$self-$$-" . $actioncount . '-' . time();

}


__END__
