# poslib.pl # # Poslib Perl interface for Zaza map applet. # # Copyright (C) 2001 Brian Rudy (brudyNO@SPAMpraecogito.com) # # 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. # # # Requires: # Config::IniFiles # IPC::ShareLite # # Needed by libposlib.so, DISPLAY_LOCALIZE, and posServer # ## Revision History ## # 0.43 8-19-2001 # Added active goal loading and comparison. Repositioned semaphore when # new voted goal is added to the active goal table. Updated getgoal() # to load multiple goals. # # 0.42 8-17-2001 # Added client timestamping and auto removal so vote tabulation only # counts current users. Added main poslib.ini reading and global # loading via loadglobals(). # # 0.41 8-16-2001 # Fleshed-out new goal semaphore and goal loading. Now operational # with basic client voting and loading to the planner. # # 0.40 8-15-2001 # Added client fingerprinting, goal voting and vote tabulation. # Added stubs for new goal semaphores. # # 0.30 8-14-2001 # Basic goal ini reading via Config::IniFiles and storage in shared # memory for posServer access. # # 0.20 8-13-2001 # Fixed position hash storage and retrieval w/ proper serialization. # # 0.10 8-10-2001 # First working version. Only impliments position retrieval. # use IPC::ShareLite; use DynaLoader; use Config::IniFiles; use Storable qw(freeze thaw); my $debug = 0; my $poslibini = "/home/brudy/posServer/poslib.ini"; my $testmode; my $goalfile; my $votingenabled; my $clientflushtimer; my @uberclients; # These default to 65,536 bytes each, so use the default # with care. my $posshare = new IPC::ShareLite( -key => 1492, -create => 'yes', -destroy => 'no', -size => 200 ) or die $!; my $goalshare = new IPC::ShareLite( -key => 1493, -create => 'yes', -destroy => 'no', -size => 5000 ) or die $!; my $voteshare = new IPC::ShareLite( -key => 1494, -create => 'yes', -destroy => 'no' ) or die $!; my $semaphoreshare = new IPC::ShareLite( -key => 1495, -create => 'yes', -destroy => 'no', -size => 8 ) or die $!; my $clientshare = new IPC::ShareLite( -key => 1496, -create => 'yes', -destroy => 'no' ) or die $!; my $globalshare = new IPC::ShareLite( -key => 1497, -create => 'yes', -destroy => 'no', -size => 500 ) or die $!; my $activegoalshare = new IPC::ShareLite( -key => 1498, -create => 'yes', -destroy => 'no' ) or die $!; # Is the given IP an uberclient? sub isuberclient { ($ip) = @_ ; my $i; loadglobals(); for $i (0 .. $#uberclients) { if ($ip eq $uberclients[$i]) { return 1; } } return 0; } # Read the main ini file and store the results in shared memory sub readposlibini { my $poslibini = new Config::IniFiles( -file => $poslibini ); # load it all into an Hash of Hashes my %MainHoH; my $i; my @uberclients; $MainHoH{testmode} = $poslibini->val( 'CGI', 'testmode' ); $MainHoH{goalfile} = $poslibini->val( 'Goals', 'goalfile' ); $MainHoH{votingenabled} = $poslibini->val( 'Voting', 'Enabled' ); $MainHoH{clientflushtimer} = $poslibini->val( 'Voting', 'ClientTimeoutInterval' ); @uberclients = $poslibini->val('CGI', 'uberclient'); for $i (0 .. $#uberclients) { $MainHoH{uberclients}{$i} = $uberclients[$i]; } $globalshare->store(freeze(\%MainHoH)); #print "\%MainHoH is " . length(freeze(\%MainHoH)) . " bytes.\n"; } # Other subs can run this to load the global variables from # shared memory sub loadglobals { my $i; my %MainHoH = %{thaw($globalshare->fetch)}; $testmode = $MainHoH{testmode}; #print "\$testmode is $testmode.\n"; $goalfile = $MainHoH{goalfile}; #print "\$goalfile is $goalfile.\n"; $votingenabled = $MainHoH{votingenabled}; #print "\$votingenabled is $votingenabled.\n"; $clientflushtimer = $MainHoH{clientflushtimer}; #print "\$clientflushtimer is $clientflushtimer.\n"; for $i (keys %{ $MainHoH{uberclients} }) { $uberclients[$i] = $MainHoH{uberclients}{$i}; #print "\$uberclients[$i] is " . $MainHoH{uberclients}{$i} . ".\n"; } } # Refresh the timestamp for given client sub touchclient { ($clientip) = @_ ; # should have exclusive access for this if ($clientip ne '') { my %clienttds = %{thaw($clientshare->fetch)}; $clienttds{$clientip} = time(); $clientshare->store(freeze(\%clienttds)); } else { print "Sorry dude, you need a valid IP address.\n"; } } # check the timestamp of each client and purge the expired ones sub purgeclients { # We need exclusive access for this my $client; my $currenttime = time(); my %clienttds = %{thaw($clientshare->fetch)}; my %votelist = %{thaw($voteshare->fetch)}; my %newclienttds; my %newvotelist; my $i; loadglobals(); for $client (keys %clienttds) { if (($clienttds{$client} + $clientflushtimer) >= $currenttime) { # Save each into a temporary hash $newclienttds{$client} = $clienttds{$client}; for $i (0 .. $#{ $votelist{$client} }) { if ($votelist{$client}[$i] == 1) { $newvotelist{$client}[$i] = 1; } } } else { print "Purging " . $client . " because it hasn't voted in " . ($currenttime - $clienttds{$client}) . " seconds.\n"; } } $clientshare->store(freeze(\%newclienttds)); $voteshare->store(freeze(\%newvotelist)); # release exclusive access } # Initializes all shared memory, and loads the config info sub poslibinit { my @empty; $empty[0] = ''; readposlibini(); # not 0, helps client debugging pushpos(1, 1, 1); flushclients(); flushsemaphores(); flushvotes(); $activegoalshare ->store(freeze(\@empty)); read_goal_file(); } # Set the current position sub pushpos { ($x, $y, $theta) = @_ ; if ($debug) { print "got x=" . $x . " ,y=" . $y . ", and theta=" . $theta . "\n"; } %Position = ('x' => $x, 'y' => $y, 'theta' => $theta); $posshare->store(freeze(\%Position)); if ($debug) { ## This is for verification only my %retpos = %{thaw($posshare->fetch)}; my $nx = %retpos->{'x'}; my $ny = %retpos->{'y'}; my $ntheta = %retpos->{'theta'}; print "now x=" . $nx . " , y=" . $ny . ", and theta=" . $ntheta ."\n"; } # this is totally arbitrary right now return $x + $y; } # Get the current position sub poppos { my %fretpos = %{thaw($posshare->fetch)}; # print "Current position is x=" . %fretpos->{'x'} . ", y=" . %fretpos->{'y'} . ", theta=" . %fretpos->{'theta'} . "\n"; %fretpos->{'x'}, %fretpos->{'y'}, %fretpos->{'theta'}; } # Load the goal file sub read_goal_file { loadglobals(); my $goalini = new Config::IniFiles( -file => $goalfile ); # load it all into an Array of Hashes my @GoalAoH; for (my $i = 0; $i <= 19; $i++) { if ($goalini->val( $i, 'Enabled' ) eq 'yes') { $GoalAoH[$i]{Name} = $goalini->val( $i, 'Name' ); $GoalAoH[$i]{URL} = $goalini->val( $i, 'URL' ); $GoalAoH[$i]{Appletx} = $goalini->val( $i, 'Appletx' ); $GoalAoH[$i]{Applety} = $goalini->val( $i, 'Applety' ); $GoalAoH[$i]{Realx} = $goalini->val( $i, 'Realx' ); $GoalAoH[$i]{Realy} = $goalini->val( $i, 'Realy' ); $GoalAoH[$i]{Enabled} = $goalini->val( $i, 'Enabled' ); #print "\$i=" . $i . " is enabled\n"; } #else { # print "\$i=" . $i . " is disabled\n"; #} } my $frozengoals = freeze(\@GoalAoH); #print "\n\$frozengoals is " . length($frozengoals) . " bytes.\n"; #print "Storing in shared memory...\n"; $goalshare->store($frozengoals); #print "...and now retrieving\n"; #my @newgoalsAoH = @{thaw($goalshare->fetch)}; # #for $j (0 .. $#newgoalsAoH) { # print "$j is { "; # for $role (keys %{ $newgoalsAoH[$j] }) { # print "$role= $newgoalsAoH[$j]{$role}\n"; # } # print "}\n"; #} } # pop the goals AoH out of shared memory sub popgoals { @{thaw($goalshare->fetch)}; } # register vote for goal sub votegoal { ($goalnum, $clientip) = @_ ; ## Kludgy Client Fingerprinting Schema (TM) ;) # %votelist is a hash of arrays, with the key as the # client's IP address and the array element as the goal # number. my %votelist = %{thaw($voteshare->fetch)}; if ($votelist{$clientip}[$goalnum] == 1) { print "no\n"; print "I already have a vote from you for that goal.\n"; } else { $votelist{$clientip}[$goalnum] = 1; print "ok\n"; print "Vote accepted.\n"; $voteshare->store(freeze(\%votelist)); } touchclient($clientip); purgeclients(); tallyvotes(); } # Tally all votes sub tallyvotes { my %votelist = %{thaw($voteshare->fetch)}; my @mrgoals = popgoals(); my $votestotal = 0; my %votetally; my $a; my $b; my $c; my $d; my $i; my $j; my $nummatches = 0; my $numoldmatches = 0; my @activegoals = @{thaw($activegoalshare->fetch)}; my @newactivegoals; for $client (keys %votelist) { for $i (0 .. $#{ $votelist{$client} }) { if ($votelist{$client}[$i] == 1) { $votetally{$i}++; } } } # how many have we got? for $j (keys %votetally) { my $votestring; if ($votetally{$j} > 1) { $votestring = 'votes'; } else { $votestring = 'vote'; } print $votetally{$j} . " $votestring for " . $mrgoals[$j]{Name} . ".\n"; $votestotal += $votetally{$j}; } if (($votestotal != 0) && ((keys %votetally) != 0)) { my $votesaverage = sprintf("%.0f", $votestotal/(keys %votetally)); print "The total number of votes is " . $votestotal . ". The average number is " . $votesaverage . ".\n"; # Compare with current active votes # Testing stuff #print "\nContents of \%votetally:\n"; #for $b (keys %votetally) { # print $b . " is " . $votetally{$b} . ".\n"; #} #print "\nContents of \@activegoals:\n"; #for $d (0 .. $#activegoals) { # print $d . " is " . $activegoals[$d] . ".\n"; #} for $b (keys %votetally) { my $match; for $a (0 .. $#activegoals) { if ($votetally{$b} >= $votesaverage) { if ($activegoals[$a] eq $b) { $nummatches++; print "Match found:" . $activegoals[$a] . "=" . $b . ".\n"; } } } } for $c (keys %votetally) { if ($votetally{$c} >= $votesaverage) { $numoldmatches++; } else { print "Nope, " . $votetally{$c} . " isn't >= " . $votesaverage . ".\n"; } } # if ($numoldmatches != $nummatches) { # We are only interested in new goals if ($numoldmatches < $nummatches) { print "A new goal has been voted, alerting the planner!\n"; $semaphoreshare->store("1"); my $index = 0; for $d (keys %votetally) { $newactivegoals[$index] = $d; $index++; } $activegoalshare->store(freeze(\@newactivegoals)); } } else { print "There are no votes!\n"; } # Testing stuff print "\$numoldmatches=$numoldmatches.\n"; print "\$nummatches=$nummatches.\n"; print "\nContents of \@newactivegoals:\n"; for $d (0 .. $#newactivegoals) { print $d . " is " . $newactivegoals[$d] . ".\n"; } } # Print current vote tabulation (for debugging) sub printvotes { my %clienttds = %{thaw($clientshare->fetch)}; my @mrgoals = popgoals(); my %votelist = %{thaw($voteshare->fetch)}; my $timenow = time(); for $client (keys %votelist) { if ($client ne '') { print "->" . $client . " last voted " . ($timenow - $clienttds{$client}) . " seconds ago.\n"; for $i (0 .. $#{ $votelist{$client} }) { if ($votelist{$client}[$i] == 1) { print $mrgoals[$i]{Name} . "\n"; } } print "\n"; } } } # Return the selected voted goal sub getgoal { ($whichone) = @_ ; my @mrgoals = popgoals(); my @activegoals = @{thaw($activegoalshare->fetch)}; return($mrgoals[$activegoals[$whichone]]{Realx}, $mrgoals[$activegoals[$whichone]]{Realy}); } # How many goals are in @activegoals? sub howmanygoals { my @activegoals = @{thaw($activegoalshare->fetch)}; return $#activegoals; } # Initialize the client shared memory space sub flushclients { my %tds; $tds{'127.0.0.1'} = time(); $clientshare->store(freeze(\%tds)); } # Flush out semaphore shared memory sub flushsemaphores { $semaphoreshare->store("0"); } # Flush out vote shared memory space sub flushvotes { my %votelist; $votelist{'127.0.0.1'}[0] = 0; $voteshare->store(freeze(\%votelist)); } # Check if a new goal has been added sub newgoal { my $check = $semaphoreshare->fetch; if ($check eq '1') { return 1; } else { return 0; } } # # Components of the following sub have been borrowed from Charles # Kereke's IpMath.pl (http://homepages.waymark.net/~ckerekes/IpMath.pl) # #This sub converts an entire ip address or subnet mask to char sub IpToBin { my ($ip) = @_[0]; #this is the ip to convert my @octets, $bin; #Split up octets into the array @octets=split(/\./,$ip); #Append the decimal value of the octets $bin = unpack("c", $octets[0]); $bin .= unpack("c", $octets[1]); $bin .= unpack("c", $octets[2]); $bin .= unpack("c", $octets[3]); #return the result in $bin $bin; } 1;