#!/usr/bin/perl use strict; use warnings; use Digest::MD5; package infobomb; ################ MAIN ################ $ARGV[0] or die "Usage: $0 config_file.conf\n"; # read config file %infobomb::config = (); read_configuration($ARGV[0]); check_configuration(); my $filename_base = "[$infobomb::config{project}]-[$infobomb::config{location}]-[$infobomb::config{'sensor-type'}]-[$infobomb::config{sn}]-[data]"; $infobomb::config{'state_file'} = "$infobomb::config{'data-path'}/$filename_base.state"; $infobomb::config{'readings_log'} = "$infobomb::config{'data-path'}/$filename_base.csv"; ############### CONFIGURATION ################ # client ssh/scp absolute paths $infobomb::ssh = "/usr/sbin/ssh"; $infobomb::scp = "/usr/sbin/scp"; # client identity file absolute path (ssh public key) $infobomb::identity_file = "/root/.ssh/pmp3_rsa"; # TODO: figure out why this doesn't get set by .bashrc or .bash_profile on host # special environment variables to set on host #$infobomb::host_export = "export DJANGO_SETTINGS_MODULE=infobomb.settings; export PYTHONPATH=/home/infobomb;"; # user and host of running infobomb instance $infobomb::user = "infobomb"; $infobomb::host = "stewie.cs.earlham.edu"; # absolute path to infobomb checkout on host #$infobomb::host_path = "/home/infobomb/infobomb/"; $infobomb::host_data = "/home/infobomb/data/"; $infobomb::ssh = "$infobomb::ssh -i $infobomb::identity_file"; $infobomb::scp = "$infobomb::scp -i $infobomb::identity_file"; #$infobomb::check_transactions = "$infobomb::host_path/dataparser/check_transactions.py"; ############### MAIN ################ # poll sensor, record reading my ($reading, $state); $state = read_state(); ++$state; record_state($state); $reading = poll($infobomb::config{'run'}); if ($reading ne 'INVALID') { record_reading($infobomb::config{'readings_log'}, $reading); } else { ErrorEvent("alert", "Sensor returned non-numeric value."); } print "state: $state\n"; if ($state >= $infobomb::config{'readings_per_file'}) { my $date = time; $state = 0; record_state($state); system("mv $infobomb::config{readings_log} $infobomb::config{'data-path'}/$filename_base-[$date].csv"); create_checksum_file("$infobomb::config{'data-path'}/$filename_base-[$date].csv"); delete_or_send_transactions(); } ############### SUBS ################ sub delete_or_send_transactions { my $filelist = `ls $infobomb::config{'data-path'} | grep csv | grep -v md5`; my @files = split("\n",$filelist); # go through each file in the data-path directory foreach my $file (@files) { # parse filename for project details my ($project, $location, $sensor_type, $sn, $type, $time) = ( $file =~ /\[([^\[\]]*)\]/g ); # check to see if file is our project if ( ($infobomb::config{'project'} eq $project) && ($infobomb::config{'location'} eq $location) && ($infobomb::config{'sensor-type'} eq $sensor_type) && ($infobomb::config{'sn'} eq $sn)) { if ($time < (time - 1209600)) { # file is older than 2 weeks, remove it system("rm -f $infobomb::config{'data-path'}/$file $infobomb::config{'data-path'}/$file.md5"); } else { #contact server to see if transaction exists in infobomb database #my $trans = `$infobomb::ssh $infobomb::user\@$infobomb::host \"$infobomb::host_export $infobomb::check_transactions $file\"`; my $download = "wget -q http://stewie.cs.earlham.edu:8000/transaction/$file -O $infobomb::config{'data-path'}/datacheck/$file"; system("$download"); my $contents = `cat $infobomb::config{'data-path'}/datacheck/$file`; #if ( $trans =~ /EXISTS/ ) { if ( $contents =~ /EXISTS/ ) { # transaction exists, remove the file print "Transaction completed. Deleting file\n"; system("rm -f $infobomb::config{'data-path'}/$file $infobomb::config{'data-path'}/$file.md5"); } else { # transaction doesn't exist, transfer file to server print "Starting transaction\n"; system("$infobomb::scp $infobomb::config{'data-path'}/$file $infobomb::config{'data-path'}/$file.md5 $infobomb::user\@$infobomb::host:$infobomb::host_data"); } # delete data check file system("rm -f $infobomb::config{'data-path'}/datacheck/$file"); } } } } sub read_state { record_state(0) unless (-e $infobomb::config{state_file}); open (STATE, $infobomb::config{state_file} ) or ErrorEvent("alert","Failed to open state file"); my $state = ; if (!$state) { $state = 0; } close STATE; return $state; } sub record_state { my ($state) = @_; open (STATE, ">$infobomb::config{state_file}") or ErrorEvent("alert","Failed to write state file"); print STATE $state; close STATE; } sub read_configuration { my ($config_file) = @_; my $line; open (CONFIGFILE, $config_file) or ErrorEvent("alert","Failed to open infobomb::config file"); while ($line = ) { if ($line =~ /^([^=]+)=(.*)/) { my ($config_name, $config_val) = ($1, $2); $infobomb::config{$config_name} = $config_val; } } close CONFIGFILE; } sub poll { my ($run) = @_; my ($time, $output); $time = time; $output = `$run`; chomp $output; if ( $output =~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/ ) { return "$time,$output"; } else { return "INVALID"; } } sub record_reading { my ($log, $reading) = @_; open (LOG, ">>$log") or ErrorEvent("alert","Failed to write log file"); print LOG "$reading\n"; close LOG; } sub check_configuration { my @keys = qw( project location sensor-type sn readings_per_file run data-path ); foreach my $key (@keys) { if (not $infobomb::config{$key}) { print "Alert: $key is undefined in infobomb::configuration file.\n"; die; } } # check for arguments associated with run command # i.e. ./poll_sensor --type=temperature my @run_args = split(/ /, $infobomb::config{'run'}); my $path = $run_args[0]; ErrorEvent("alert", "Failed to open sensor script/binary") unless (-e $path); ErrorEvent("alert", "Failed to open data-path.") unless (-e $infobomb::config{'data-path'}); } sub ErrorEvent { my ( $type, $message ) = @_; my $time = time; my $filename = "$infobomb::config{'data-path'}/[$infobomb::config{project}]-[$infobomb::config{location}]-[$infobomb::config{'sensor-type'}]-[$infobomb::config{sn}]-[$type]-[$time].csv"; print "$type: $message\n"; open(LOG, ">>$filename") or die "Failed to open error file"; print LOG $message; close LOG; create_checksum_file_or_die($filename); if ($type eq 'alert') { die; } } sub create_checksum_file_or_die { my ($filename) = @_; open(DATAF, $filename) or die "Failed to open data file."; binmode(DATAF); open(CSF, ">>$filename.md5") or die "Failed to open checksum file."; print CSF Digest::MD5->new->addfile(*DATAF)->hexdigest; close CSF; close DATAF; } sub create_checksum_file { my ($filename) = @_; open(DATAF, $filename) or ErrorEvent("alert","Failed to open data file."); binmode(DATAF); open(CSF, ">>$filename.md5") or ErrorEvent("alert","Failed to open checksum file."); print CSF Digest::MD5->new->addfile(*DATAF)->hexdigest; close CSF; close DATAF; }