###########################################
package NmapServer;
###########################################
# Daemon running nmap periodically and 
# serving JSON data via a web interface
# Mike Schilli, 2014 (m@perlmeister.com)
###########################################
use strict;
use warnings;
use Log::Log4perl qw(:easy);
use AnyEvent;
use AnyEvent::HTTPD;
use JSON qw( to_json );
use File::Temp qw( tempfile );
use XML::Simple;

###########################################
sub new {
###########################################
  my( $class, %options ) = @_;

  my( $fh, $tmp_file ) = 
     tempfile( UNLINK => 1 );

  my $self = {
    xml_file   => $tmp_file,
    fork       => undef,
    json       => "",
    child      => undef,
    scan_range => [],
    %options,
  };

  bless $self, $class;
}

###########################################
sub start {
###########################################
  my( $self ) = @_;

  $self->{ timer } = AnyEvent->timer(
    after    => 0, 
    interval => 3600, 
    cb => sub { 
      if( defined $self->{ fork } ) {
          DEBUG "nmap already running";
          return 1;
      }
      $self->nmap_spawn();
    },
  );

  $self->httpd_spawn();
}

###########################################
sub nmap_spawn {
###########################################
  my( $self ) = @_;

  $self->{ fork } = fork();

  if( !defined $self->{ fork } ) {
    LOGDIE "Waaaah, failed to fork!";
  }

  if( $self->{ fork } ) {
      # parent
    $self->{ child } = AnyEvent->child( 
      pid => $self->{ fork }, 
      cb  => sub {
        my $data = 
          XMLin( $self->{ xml_file });
        $self->{ json } = 
         to_json( $data, { pretty => 1 } ); 
        $self->{ fork } = undef;
      } );
  } else {
      # child
    exec "nmap", "-oX", 
      $self->{ xml_file }, 
      @{ $self->{ scan_range } },
  }
}

###########################################
sub httpd_spawn {
###########################################
  my( $self ) = @_;

  $self->{ httpd } = 
    AnyEvent::HTTPD->new( port => 9090 );

  $self->{ httpd }->reg_cb (
    '/' => sub {
      my ($httpd, $req) = @_;

      $req->respond({ content => 
        ['text/json', $self->{ json } ],
      });
    },
  );
}

1;
