CoolRaoul

presence detection (Synology)

Dec 30th, 2015
173
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 4.21 KB | None | 0 0
  1. #! /usr/bin/perl
  2. use warnings;
  3. use strict;
  4. use Getopt::Std;
  5. use POSIX qw (setsid strftime);
  6. use File::Path qw (mkpath);
  7. use Net::Ping;
  8. $ENV{PATH} = "/bin:/usr/bin";
  9.  
  10. # makes END() block called by signals
  11. # ref: http://perldoc.perl.org/perlfaq8.html#How-can-I-do-an-atexit()-or-setjmp()%2flongjmp()%3f-(Exception-handling)
  12. use sigtrap qw(die normal-signals);
  13.  
  14. my @devices;
  15.  
  16. chdir "/tmp";
  17.  
  18. sub daemonize {
  19.    POSIX::setsid or die "setsid: $!";
  20.    my $pid = fork ();
  21.    if ($pid < 0) {
  22.       die "fork: $!";
  23.    } elsif ($pid) {
  24.       exit 0;
  25.    }
  26.    open (STDIN, "</dev/null");
  27.    open (STDOUT, ">/dev/null");
  28.    open (STDERR, ">&STDOUT");
  29. }
  30.  
  31. #
  32. # c: ping count (default=2)
  33. # w: ping timeout seconds(default=2)
  34. # i: ping interval seconds (default=60)
  35. # D
  36. my $optswitches = "";
  37. my %opts = (
  38.     v => undef,                 # verbose
  39.     d => undef,                 # daemonize
  40.     w => 2,
  41.     c => 2,
  42.     i => 60,                    # ping interval
  43.     D => "/var/run/presence.d",
  44.     H => "/var/log/presence-%Y-%m.tsv",
  45.     C =>  "/etc/presence.conf",
  46.     W => undef,                 # wipe (erase history)
  47.     );
  48.  
  49.  
  50. map {
  51.     $optswitches .= $_;
  52.     $optswitches .= ":" if defined($opts{$_});
  53. } keys %opts ;
  54. getopts($optswitches, \%opts) or die "invalid option(s)\n";  # options as above. Values in %opts
  55.  
  56. my $verbose = $opts{v};
  57. my $STATEDIR = $opts{D};
  58. my $HISTFILE = $opts{H};
  59. my $CONF = $opts{C};
  60. my $PIDFILE = "/var/run/presence.pid";
  61. mkpath $STATEDIR;
  62.  
  63. #+
  64. #   Parse config file
  65. #-
  66. open my $conf, "<", $CONF or die "$! opening $CONF\n";
  67. while (<$conf>) {
  68.     s/#.*$//;
  69.     next unless /^\s*(\S+)\s+(.*)\s*$/;
  70.     push @devices, {
  71.         IP => $1,
  72.         name => $2,
  73.         state => "unknown",
  74.     };
  75. };
  76.  
  77. daemonize if $opts{d};
  78. open my $pidfile_h, ">", $PIDFILE or die "$! creating $PIDFILE\n";
  79. $pidfile_h->print("$$\n");
  80. $pidfile_h->close();
  81.  
  82. #+
  83. #   check device  background, returns pid
  84. #-
  85. sub check {
  86.     my $pid = fork ();
  87.     if ($pid < 0) {
  88.         die "fork: $!";
  89.     };
  90.     return $pid if $pid;
  91.    
  92.     #+
  93.     #   Forked
  94.     #-
  95.     $PIDFILE = undef;   # prevent removing of PID file by background processes
  96.     my $device = shift;
  97.     my $IP = $device->{IP};
  98.     my $count = $opts{c};
  99.     my $timeout = $opts{w};
  100.     my $p = Net::Ping->new("icmp");
  101.     while ($count) {
  102.         exit 0 if $p->ping($IP, $timeout);
  103.         $count--;
  104.     }
  105.     exit 1;
  106. }
  107.  
  108. #+
  109. #   variable history file name using strftime
  110. #-
  111. sub histfile_name {
  112.         return  POSIX::strftime($HISTFILE, localtime);
  113. }
  114.  
  115. my $histfile_h;
  116.  
  117. # wipe history?
  118. if ($opts{W}) {
  119.     my $name = histfile_name;
  120.     open $histfile_h, ">", $name or die "$! opening $name\n";
  121.     close $histfile_h;
  122. };
  123.  
  124. #+
  125. #    Main Loop
  126. #-
  127. for (;;) {
  128.     my %deviceforpid;
  129.    
  130.     #+
  131.     #   fork pings threads in background
  132.     #-
  133.     foreach my $device (@devices) {
  134.         my $pid = check  $device;
  135.         $deviceforpid{$pid} = $device;
  136.     }
  137.  
  138.     #+
  139.     #   waits for results and update state files
  140.     #-
  141.     my $histfile_name = histfile_name;
  142.     open my $histfile_h, ">>", $histfile_name or die "$! opening $histfile_name\n";
  143.     while (my $pid = wait()) {
  144.         last if $pid < 0;
  145.         my $status=$?;
  146.  
  147.         my $device = $deviceforpid{$pid};
  148.         my $state  = $status == 0 ? "on" : "off";
  149.         print STDERR "$device->{IP} ($device->{name}), status=$status, state=$state\n" if $verbose;
  150.         next if ($device->{state} eq $state); # not changed since last pass
  151.         $device->{state}  = $state;
  152.  
  153.         #+
  154.         #   update per device state file
  155.         #-
  156.         my $statefile = $STATEDIR . "/" . $device->{name} . ".state";
  157.         open my $statefile_h, ">", "$statefile" or die "$! while opening $statefile\n";
  158.         $statefile_h->print("$state\n");
  159.  
  160.         #+
  161.         #   update history file
  162.         #-
  163.         my $timestamp=POSIX::strftime("%F %T", localtime);
  164.         $histfile_h->print(join("\t", $timestamp, $device->{name}, $state));
  165.         $histfile_h->print("\n");
  166.  
  167.     }
  168.     close($histfile_h);
  169.     print STDERR "sleeping...\n" if $verbose;
  170.     sleep $opts{i};
  171. }
  172.  
  173.  
  174. 1;
  175.  
  176. END {
  177.     unlink $PIDFILE if defined $PIDFILE;
  178. };
  179.  
  180. # Local Variables:
  181. # mode: perl
  182. # End:
Add Comment
Please, Sign In to add comment