#!/usr/bin/perl -w

################################################
#
# SMS Dispatcher Server v0.3
#
# Written by: Michael Fung http://www.3open.org/
# Last Update: 2011-01-30
#
################################################


# load required modules
use strict;
use POSIX;
use DBI;
use Getopt::Long;
require Crypt::SSLeay;
use LWP::UserAgent;


# declare config file customizable variables
our ( $dbhost, $dbname, $dbuser, $dbpw,
      $gw_userid, $gw_passwd, $gw_sendername,
      $max_jobs    
    );
  
my $lockfile = "/var/run/smsd.pid";
my $logfile = "/var/log/smsd.log";
my $batch_interval = 30;  # wait between each batch, in seconds
my $dbconn_retry_interval = 60;  # seconds
my $min_process_interval = 10;  # minimum time between each tries in seconds
my $GNOKII = '/usr/bin/gnokii';


# read in config file
do "/etc/smsd.conf" || die "Error reading configuration file!\n";

my $dsn = "DBI:mysql:database=$dbname;host=$dbhost;mysql_socket=/var/lib/mysql/mysql.sock";	

# declare variables
my (	$s, $daemon, $verbose, $help, $usage,
		$dbh, $now, $sql, $p, $q, $pnumrows, $qnumrows, $prow, $qrow,
		$id, $hostgroup, $hostname, $sms_text, $phone_no, $sent, $error, $quota_id,
		$last_process_time, $temp, $last_24hr
	);	
my $start_time = time();		
my $gw_errors = 0;
my $local_errors = 0;
my $version = "0.3";
		
# define subs:
sub printlog {
	my $msg = shift;
	my $date_text = strftime "%Y-%m-%d %H:%M:%S", localtime;
	my $logtext = "[" . $date_text . "] $msg\n";
	
	if ($daemon) {
		open(LOGFP, ">> $logfile");
		print LOGFP "$logtext";
		close(LOGFP);
	} else {
		print STDERR $logtext;
	}
} 


# send via Meteors SMS Gateway
sub send_gw {

	my ($phone_no, $sms_text) = @_;
	# url encode the sms text
	$sms_text =~ s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;	
	my $url = "https://www.meteorsis.com/f_sendsms.aspx?username=$gw_userid&password=$gw_passwd&content=$sms_text&langeng=1&recipient=$phone_no&dos=now&senderid=$gw_sendername";
	printlog("Gateway request: $url") if $verbose;
	
	my $ua = LWP::UserAgent->new;
	$ua->agent("SMS-Dispatcher/$version");
	$ua->max_size(1024);
	$ua->max_redirect(0);

	my $response = $ua->get($url);
	if ($response->is_success) {
		if ($response->content =~ /SMSDID:/) {			
			$gw_errors = 0;  # reset
			return 1;
		} else {
			$gw_errors++;
			printlog("Gateway server returned error: ". $response->content );
			printlog("Consecutive errors count: $gw_errors");
			return 0;
		}
	
	} else {		
		$gw_errors++;
		printlog("Gateway server request failed: " . $response->status_line );
		printlog("Consecutive errors count: $gw_errors");
		return 0;
	}
} 

# send via local cell phone
sub send_local {
	my ($phone_no, $sms_text) = @_;
	
	if (system("/bin/echo '$sms_text' | $GNOKII --sendsms $phone_no") == 0) {
		$local_errors = 0;
		return 1;
	} else {
		$local_errors++;
		return 0;	
	}
}

# program exit
sub prg_exit {	
    printlog("*** SMS Dispatcher Shutdown ***");
    unlink "$lockfile";
    exit;
}
$SIG{HUP} = \&prg_exit;
$SIG{TERM} = \&prg_exit;
$SIG{INT} = \&prg_exit;

sub syntax {
	$s = shift or $s = 'Unknown';
	$usage = <<EOT;
SMS Dispatcher Server v$version
(C) 2010 Michael Fung http://www.3open.org/ All rights reserved.
Licensed under the GPL.

Syntax: smsd.pl [-h] [-d] [-v] 
  -h              Show help  
  -d              Run in daemon mode
  -v              Verbose output
EOT

	printf STDERR ("Error: $s\n\n") unless ($help);
	print STDERR $usage;
	exit(0) if $help;
	exit(1);
}

# database connection
sub dbconn {
	printlog("Connecting to database...") if $verbose;
	
	# check if already connected
	return 1 if (defined($dbh) && $dbh->{Active});
	
	if ($dbh = DBI->connect($dsn, $dbuser, $dbpw, { RaiseError => 0, AutoCommit => 1 })) {
		return 1;
	} else {
		printlog("$DBI::errstr");
		return 0;
	}
}

sub set_done {
	my ($id, $error) = @_;	
	$dbh->do("update sms_queue set done='1', error='$error', last_update='$now' where id='$id'");
}


# daemonize
sub daemonize {
	if (my $pid = fork) { exit 0; }  # exit the parent process
	POSIX::setsid() or die "FATAL ERROR: Can't daemonize!";

    chdir "/";
    umask 0;
    open(STDIN,  "+>/dev/null");
    open(STDOUT, "+>&STDIN");
    open(STDERR, "+>&STDIN");
}

###################
###################
# 	Main Entry
###################
###################

# avoid duplicate process
if (-e $lockfile) {
    print STDERR "ERROR: Another copy of smsd is running\n";
    exit;
}


Getopt::Long::Configure('bundling');
GetOptions(	
	"v" => \$verbose,
	"d" => \$daemon,
	"h" => \$help, "help" => \$help
) || syntax("Invalid option(s)");


# syntax checking
syntax if ($help);

printlog("*** SMS Dispatcher Server v$version started ***");


# daemonize
if ($daemon) {
	daemonize();  # switch to daemon mode
	printlog("Switched to the background, pid:$$") if $verbose;
}

# setup lock file
open(LF, "> $lockfile");
print LF "$$\n";
close(LF);


# begin endless loop
while (1) {

	# connect to database
	if (!dbconn()) {
		printlog("Database connection failed!");		
		#prg_exit;
		sleep($dbconn_retry_interval);
		next;
	};
	
	# clear expired jobs
	$now = time();
	$dbh->do("update sms_queue set done='1', error='Job expired.', last_update='$now' where ('$now' > not_after) and (done !='1')");

    # check jobs sent in last 24hrs
    $last_24hr = time() - 86400;
    $sql = <<SQL;
select count(id) as jobs from sms_done_queue
where ('sent_date' > '$last_24hr')
  and (sent = '1')
SQL

	printlog("Check last 24hrs jobs SQL: $sql") if $verbose;
	$q = $dbh->prepare($sql);
	$q->execute;
	$qrow = $q->fetchrow_hashref;
	$jobs = $qrow->{jobs};
	if ($jobs > $max_jobs) {
       printlog("WARNING: Max jobs per day reached. Jobs done: $jobs.");       
       # Something is wrong, adjust batch interval to 60 minutes
       # optionally alert administrator
       # and don't process the queue              
       $batch_interval = 3600;
		
       # disconnect db and take a nap
	   $q->finish;
	   $dbh->disconnect;
       sleep($batch_interval);		
       next; 
    }
    
    printlog("Jobs done in last 24hrs: $jobs.") if $verbose;
	
    # get jobs to be processed		
	$now = time();	
	$last_process_time = $now - $min_process_interval;  # prevent fast looping
	$sql = <<SQL;
select * from sms_queue 
where ('$now' < not_after or not_after is null) 
  and ('$now' > not_before or not_before is null)
  and (last_update < '$last_process_time' or last_update is null)
  and (done != '1')
SQL
	
	printlog("Get jobs SQL: $sql") if $verbose;
	$q = $dbh->prepare($sql);
	$q->execute;
	$qnumrows = $q->rows;	
	printlog("Messages to process: $qnumrows") if $verbose;	
	
	# do some housekeeping and sleep some time if no jobs
	if ($qnumrows == 0) {
		# housekeeping: relocate done jobs		
		$dbh->do("lock tables sms_queue write, sms_done_queue write");
		if ($dbh->do("insert into sms_done_queue select * from sms_queue where sms_queue.done='1'") > 0) {
			$dbh->do("delete from sms_queue where done='1'");
		}
		$dbh->do("unlock tables");
		
		# disconnect db and take a nap
		$q->finish;
		$dbh->disconnect;
		sleep($batch_interval);		
		next; 
	}

	# loop through each message
	while ($qrow = $q->fetchrow_hashref) {

		$id = $qrow->{id};
		$phone_no = $qrow->{phone_no};
		$sms_text = $qrow->{sms_text};
		$sent = $qrow->{sent};
		$now = time();
		$error = '';
		
		printlog("Processing ID:$id") if $verbose;
		
		# catch inconsistency
		if ($sent) {
			printlog("ID:$id OK. Already sent, just mark it as done.");
			set_done($id, $error);
			next;
		}

		# try to send			
		if (send_gw($phone_no, $sms_text)) {			
			printlog("ID:$id OK. Sent via external gateway.");
			$now = time();
			$dbh->do("update sms_queue set done='1', sent='1', sent_date='$now', send_via='G', tries=tries+1, last_update='$now' where id='$id'");						
		}
		elsif (send_local($phone_no, $sms_text)) {
			printlog("ID:$id OK. Sent via local cell phone.");
			$now = time();
			$dbh->do("update sms_queue set done='1', sent='1', sent_date='$now', send_via='L', tries=tries+1, last_update='$now' where id='$id'");						
		}
		else {
			printlog("ID:$id ERROR: Send failed with all methods.");
			$now = time();
			$dbh->do("update sms_queue set tries=tries+1, last_update='$now' where id='$id'");									
		}
								

	} # end db records loop
	
	# clean up
	$q->finish;	

} # end endless loop
nagios/smsd_0.3.txt · Last modified: 2011-01-29 21:31 by admin
Back to top
GNU Free Documentation License 1.3
chimeric.de = chi`s home Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0