package Guardian::Socket;
use strict;
use warnings;

use Exporter qw(import);

our @EXPORT_OK = qw(Server Client Message_Parser);

use IO::Socket::UNIX qw( SOCK_STREAM SOMAXCONN );

# The path and filename to the UNIX socket.
my $socketpath = "/run/guardian";
my $socketfile = "/run/guardian/guardian.sock";

# Hash with all supported socket commands and events which should
# be returned by the parser.
my %supported_commands = (
	'block' => 'block',
	'unblock' => 'unblock',
	'flush' => 'flush',
	'reload' => 'reload',
	'logrotate' => 'logrotate',
);

#
## Socket server function.
#
## It is used to create the server component to provide an IPC
## mechanism for guardian.  The server function creates an UNIX
## socket.
#
sub Server () {
	# If the path for the socketfile does not exist, try to
	# create it.
	unless (-d "$socketpath") {
		mkdir("$socketpath") or die "Could not create $socketpath: $!\n";
	}

	# Delete an existing socket file.
	if (-e "$socketfile") {
		unlink $socketfile
		or die "Could not release existing socket file: $!\n";
	}

	# Create a new UNIX socket.
	my $server = IO::Socket::UNIX->new(
		Local => $socketfile,
		Listen => SOMAXCONN,
		Type => SOCK_STREAM,
	) or die "Could not create socket: $!\n";

	# Return the server object.
	return $server;
}

#
## A client for the socket server.
#
## This function provides a simple client to connect to the server socket
## and send messages through it to a running guardian process.
#
sub Client($) {
	my ($message) = @_;

	# Create the client and connect to the server socket.
	my $client = IO::Socket::UNIX->new(
		Type => SOCK_STREAM,
		Peer => $socketfile,
	) or die "Could not connect to socketfile: $!\n";

	# Remove any newline.
	chomp($message);

	# Send the message through the socket.
	print $client "$message\n";
}

#
## The Socket message parser.
#
## This subfunction is responsible for parsing any data or messages
## which have been recieved through the server socket.
#
sub Message_Parser ($) {
	# Splitt the message into single parts.
	# The first part contains the command, the second
	# one an optional argument.
	my @message = split(/ /, $_[0]);
	my ($command, $optarg, $unsupported) = @message;

	# Currently only socket messages with two arguments (command and
	# the additional argument which is required for some commands)
	# are supported. If a third argument is passed, the message
	# is invalid and the parser returns false.
	if ($unsupported) {
		return undef;
	}

	# Check if we got a supported command.
	if (exists $supported_commands{$command}) {
		# Check an optional argument has been given.
		if ($optarg) {
			# The message is valid, return the event and
			# the optional argument.
			return "$supported_commands{$command} $optarg";
		} else {
			# Only return the event for the recieved command.
			return "$supported_commands{$command}";
		}
	}

	# If we got here, the command was not supported or something different
	# went wrong. In such cases the function will return undef (False).
	return undef;
}

1;
