#!/usr/bin/perl
#-----------------------------------------------------------------------------
#
#  Tirex Tile Rendering System
#
#  tirex-send
#
#-----------------------------------------------------------------------------
#  See end of this file for documentation.
#-----------------------------------------------------------------------------
#
#  Copyright (C) 2010  Frederik Ramm <frederik.ramm@geofabrik.de> and
#                      Jochen Topf <jochen.topf@geofabrik.de>
#  
#  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.
#  
#  You should have received a copy of the GNU General Public License
#  along with this program; If not, see <http://www.gnu.org/licenses/>.
#
#-----------------------------------------------------------------------------

use strict;
use warnings;

use Errno;
use Getopt::Long qw( :config gnu_getopt );
use IO::Socket;
use Pod::Usage qw();
use Socket;

use Tirex;

#-----------------------------------------------------------------------------
# Reading command line and config
#-----------------------------------------------------------------------------

my %opts = ();
GetOptions( \%opts, 'help|h', 'debug|d', 'config|c=s', 'to|t=s', 'wait|w=i' ) or exit(2);

if ($opts{'help'})
{
    Pod::Usage::pod2usage(
        -verbose => 1,
        -msg     => "tirex-send - send messages to a tirex daemon\n",
        -exitval => 0
    );
}

$Tirex::DEBUG = 1 if ($opts{'debug'});

my $config_dir = $opts{'config'} || $Tirex::TIREX_CONFIGDIR;
my $config_file = $config_dir . '/' . $Tirex::TIREX_CONFIGFILENAME;
Tirex::Config::init($config_file);

#-----------------------------------------------------------------------------

$opts{'to'} ||= 'master';

my $socket;
if ($opts{'to'} eq 'master')
{
    my $master_socket_name = Tirex::Config::get('socket_dir', $Tirex::SOCKET_DIR) . '/master.sock';
    $socket = IO::Socket::UNIX->new(
        Type  => SOCK_DGRAM,
        Local => '',
        Peer  => $master_socket_name,
    ) or die("Cannot open connection to master: $!\n");
} elsif ($opts{'to'} eq 'syncd') {
    $socket = IO::Socket::INET->new( LocalAddr => 'localhost', Proto => 'udp' );
    $socket->connect( Socket::pack_sockaddr_in($Tirex::SYNCD_UDP_PORT, Socket::inet_aton('localhost')) );
} elsif ($opts{'to'} =~ /^[0-9]+$/) {
    $socket = IO::Socket::INET->new( LocalAddr => 'localhost', Proto => 'udp' );
    $socket->connect( Socket::pack_sockaddr_in($opts{'to'}, Socket::inet_aton('localhost')) );
} else {
    print STDERR "Unknown destination for --to option: $opts{'to'}\n";
    exit(4);
}

#-----------------------------------------------------------------------------

my $DEFAULT_TIMEOUT = 5;
my $timeout = defined $opts{'wait'} ? $opts{'wait'} :
                    ($opts{'to'} eq 'syncd' ? 0 : $DEFAULT_TIMEOUT);

#-----------------------------------------------------------------------------

my $type = shift;

if (! $type)
{
    print STDERR "missing message type\n";
    exit(4);
}

my %msg  = ( type => $type );

# don't add id if we are not waiting for response
$msg{'id'} = "tirex-send.$$" unless ($timeout == 0);

foreach my $field (@ARGV)
{
    my ($k, $v) = split(/=/, $field, 2);
    $msg{$k} = $v;
}

#-----------------------------------------------------------------------------

my $request = Tirex::Message->new(%msg);
print STDERR "sending: ", $request->to_s(), "\n" if ($Tirex::DEBUG);
$request->send($socket);

exit(0) if ($timeout == 0);

local $SIG{ALRM} = sub { print STDERR "timeout waiting for answer\n"; exit(3) };
alarm($timeout);

my $reply = Tirex::Message->new_from_socket($socket);
if (! defined $reply)
{
    if ($!{'ECONNREFUSED'})
    {
        print STDERR "Could not send request to server. Is it running?\n";
    }
    else
    {
        print STDERR "Error reading answer: $!\n";
    }
    exit(3);
}

print STDERR "got answer: ", $reply->to_s(), "\n" if ($Tirex::DEBUG);

if ($reply->unknown_message_type())
{
    print STDERR "unknown message type: $type\n";
    exit(2);
}

exit($reply->ok() ? 0 : 1);


__END__

=head1 NAME

tirex-send - send messages to a tirex daemon

=head1 SYNOPSIS

tirex-send [OPTIONS] TYPE [KEY=VALUE ...]

=head1 OPTIONS

=over 8

=item B<-h>, B<--help>

Display help message.

=item B<-d>, B<--debug>

Run in debug mode. You'll see the actual messages sent and received.

=item B<-c>, B<--config=DIR>

Use the config directory DIR instead of /etc/tirex.

=item B<-t>, B<--to=DESTINATION>

Talk to DESTINATION, which can be 'master' (default), 'syncd', or a port
number.

=item B<-w>, B<--wait=SECONDS>

Wait SECONDS for the answer after sending the message. Default is 5 seconds.
If set to 0, don't wait for an answer at all. This is the default when the
destination is set to syncd.

=back

=head1 DESCRIPTION

tirex-send sends a message through a UDP socket to a tirex daemon running on
the local machine and waits for the answer. It can be used to check whether a
daemon is running, activate debugging in the daemon, send a render request,
etc.

Messages are normally sent to the tirex-master daemon, but can also be sent
to a sync daemon or one of the rendering backends.

You have to give the TYPE of the message on the command line and optionally
some fields in the format KEY=VALUE.

=head1 MESSAGES FOR MASTER

The following messages can be send to a master daemon:

=over 8

=item ping

Check whether server is alive.

=item debug

Activate debugging in the daemon.

=item nodebug

Deactivate debugging in the daemon.

=item quit

Quit server now (unclean!).

=item shutdown

Clean shutdown (no yet implemented XXX)

=item reload_config

Reload renderer and map config.

=item reset_max_queue_size

Reset max queue size indicator in shared memory.

=item stop_rendering_bucket

Stop feeding jobs from a bucket to the renderer. Currently rendering jobs are unaffected.
Give the name of the bucket as "bucket=NAME".

=item continue_rendering_bucket

Continue rendering jobs from this bucket.
Give the name of the bucket as "bucket=NAME".

=item metatile_enqueue_request

Add metatile to job queue.

=item metatile_remove_request

Remove job from queue.

=back

=head1 MESSAGES FOR SYNCD

The following messages can be send to a syncd server:

=over 8

=item metatile_render_request

Syncs the metatile file.

=back

=head1 MESSAGES TO BACKENDS

The following messages can be send to a rendering backend:

=over 8

=item metatile_render_request

Renders the metatile requested.

=back

=head1 FILES

=over 8

=item F</etc/tirex/tirex.conf>

The configuration file.

=back

=head1 DIAGNOSTICS

tirex-send returns

=over 8

=item 0 if the message was answered with 'ok',

=item 1 if the message was answered with an error,

=item 2 if the error was 'unknown_message_type',

=item 3 if the message could not be sent or if there was a timeout waiting for the answer,

=item 4 if there was an error parsing the command line.

=back

=head1 SEE ALSO

L<http://wiki.openstreetmap.org/wiki/Tirex>

=head1 AUTHORS

Frederik Ramm <frederik.ramm@geofabrik.de>, Jochen Topf
<jochen.topf@geofabrik.de> and possibly others.

=cut

#-- THE END ----------------------------------------------------------------------------
