#! /usr/bin/perl
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2009,2019 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 

# @(#)90   1.10   src/rsct/caa/test/ahafs_sim_multi.perl, topology.services, rsct_rady, rady2035a 1/13/10 12:56:45

use IO::Socket;
use Getopt::Long;

my $adapter, $node, $process, $reconfig, $help;

if (!GetOptions("adapter" => \$adapter,
				"node" => \$node,
				"process" => \$process,
				"reconfig" => \$reconfig,
				"help" => \$help))
{
    usage(1);
}


my $adapter_liveness_path, $node_liveness_path, $process_liveness_path;
my $cluster_reconfig_path;


if ($adapter) {
	$adapter_liveness_path = "/tmp/Cluster/{adapter_events";
}

if ($node) {
	$node_liveness_path = "/tmp/Cluster/{node_events";
}

if ($process) {
	$process_liveness_path = "/tmp/Cluster/{process_events";
}

if($reconfig) {
	$cluster_reconfig_path = "/tmp/Cluster/{reconfig_events";
}

if (0 == $adapter + $node + $process + $reconfig) {
	#
	# At least one event type must be specified.
	#

	usage(1);
}

STDOUT->autoflush(1);

#
# Create the unix domain sockets for specified event types.
#

my $adapter_liveness_socket;
my $node_liveness_socket;
my $process_liveness_socket;
my $cluster_reconfig_socket;

#
# Ready the directory that contains the socket file.
#

system("mkdir -p /tmp/Cluster 1>/dev/null 2>/dev/null");

#
# Start with a clean socket file situation.
#

remove_socket_files();

#
# Cleanup socket files on Ctrl-C.
#

$SIG{INT} = \&cleanup;

#
# Track how many clients are required to connect (one per path).
#

my $num_clients_required = 0;

if ($adapter_liveness_path) {
	$adapter_liveness_socket = new IO::Socket::UNIX(Local => $adapter_liveness_path,
					Listen => 1, Reuse => 1)
					or die sprintf("%d: couldn't create the socket", __LINE__);

	$adapter_liveness_socket->autoflush(1);

	$num_clients_required++;
}

if ($node_liveness_path) {
	$node_liveness_socket = new IO::Socket::UNIX(Local => $node_liveness_path,
					Listen => 1, Reuse => 1)
					or die sprintf("%d: couldn't create the socket", __LINE__);

	$node_liveness_socket->autoflush(1);

	$num_clients_required++;
}

if ($process_liveness_path) {
	$process_liveness_socket = new IO::Socket::UNIX(Local => $process_liveness_path,
					Listen => 1, Reuse => 1)
					or die sprintf("%d: couldn't create the socket", __LINE__);

	$process_liveness_socket->autoflush(1);

	$num_clients_required++;
}

if ($cluster_reconfig_path) {
	$cluster_reconfig_socket = new IO::Socket::UNIX(Local => $cluster_reconfig_path,
					Listen => 1, Reuse => 1)
					or die sprintf("%d: couldn't create the socket", __LINE__);

	$cluster_reconfig_socket->autoflush(1);

	$num_clients_required++;
}


#
# Accept one client for each socket. Do this by going into a select loop
# until all specified sockets have a client. The select will pop for each
# socket as clients thereof connect.
#

my $adapter_liveness_client;
my $node_liveness_client;
my $process_liveness_client;
my $cluster_reconfig_client;

#
# The input and output select vectors for read file descriptors.
# The input vector tells select what file descriptors to sit on.
# The output vector tells caller what file descriptors have been
# selected because a client has connected.
#

my $readFds = '';
my $readFdsOut = '';

if ($adapter_liveness_socket) {
	vec($readFds, fileno($adapter_liveness_socket), 1) = 1;
}

if ($node_liveness_socket) {
	vec($readFds, fileno($node_liveness_socket), 1) = 1;
}

if ($process_liveness_socket) {
	vec($readFds, fileno($process_liveness_socket), 1) = 1;
}

if ($cluster_reconfig_socket) {
	vec($readFds, fileno($cluster_reconfig_socket), 1) = 1;
}

#
# Keep track of how many required clients have connected to know
# when to stop waiting via select.
#

my $num_clients_connected = 0;

#
# As clients connect, accept their connections.
#

while ($num_clients_connected < $num_clients_required) {
	my $numPop = select($readFdsOut = $readFds, undef, undef, undef) or die sprintf("%d: ooops rc(%d)\n", __LINE__, $?);

	if ($adapter_liveness_socket && (1 == vec($readFdsOut, fileno($adapter_liveness_socket), 1))) {
		$adapter_liveness_client = $adapter_liveness_socket->accept();
		$num_clients_connected++;
		print "adapter liveness client has connected\n";
	}

	if ($node_liveness_socket && (1 == vec($readFdsOut, fileno($node_liveness_socket), 1))) {
		$node_liveness_client = $node_liveness_socket->accept();
		$num_clients_connected++;
		print "node liveness client has connected\n";
	}

	if ($process_liveness_socket && (1 == vec($readFdsOut, fileno($process_liveness_socket), 1))) {
		$process_liveness_client = $process_liveness_socket->accept();
		$num_clients_connected++;
		print "process liveness client has connected\n";
	}

	if ($cluster_reconfig_socket && (1 == vec($readFdsOut, fileno($cluster_reconfig_socket), 1))) {
		$cluster_reconfig_client = $cluster_reconfig_socket->accept();
		$num_clients_connected++;
		print "cluster reconfig client has connected\n";
	}
}

#
# Verify the clients are requesting the right stuff.
#

my $expected_request = "CHANGED=YES;CLUSTER=YES";

if ($adapter_liveness_client) {
	my $adapter_liveness_request = <$adapter_liveness_client>;

	chomp $adapter_liveness_request;

	if ($adapter_liveness_request ne $expected_request) {
		print "ERROR: adapter liveness client: expected_request($expected_request) actual_request($adapter_liveness_request)\n";
		exit 1;
	}
}

if ($node_liveness_client) {
	my $node_liveness_request = <$node_liveness_client>;

	chomp $node_liveness_request;

	if ($node_liveness_request ne $expected_request) {
		print "ERROR: node liveness client: expected_request($expected_request) actual_request($node_liveness_request)\n";
		exit 1;
	}
}

if ($process_liveness_client) {
	my $process_liveness_request = <$process_liveness_client>;

	chomp $process_liveness_request;

	if ($process_liveness_request ne $expected_request) {
		print "ERROR: process liveness client: expected_request($expected_request) actual_request($process_liveness_request)\n";
		exit 1;
	}
}

if ($cluster_reconfig_client) {
	my $cluster_reconfig_request = <$cluster_reconfig_client>;

	chomp $cluster_reconfig_request;

	if ($cluster_reconfig_request ne $expected_request) {
		print "ERROR: cluster reconfig request: expected_request($expected_request) actual_request($cluster_reconfig_request)\n";
		exit 1;
	}
}

sub common_input {
	local ($path) = @_;   
	print $path "BEGIN_EVENT_INFO\n";
	print $path "RC_FROM_EVPROD=0\n";
	print $path "SEQUENCE_NUM=0\n";
	print $path "TIME_tvsec=1244830365\n";
	print $path "TIME_tvnsec=637528480\n";
	print $path "PID=360544\n";
	print $path "UID=0\n";
	print $path "UID_LOGIN=0\n";
	print $path "GID=0\n";
	print $path "PROG_NAME=ahafs_sim_multi\n";  
	print $path "BEGIN_EVPROD_INFO\n";
} 


print "Ready for input ...\n";
if ($node_liveness_client || $adapter_liveness_client || $process_liveness_client) {
	print "  \"n/a/p u/d node_number [node_id sequence_number reason_code|command_name interface_name] |\"\n";
}
if ($cluster_reconfig_client) {
	print "  \"r [sequence_number reason_code] |\"\n";
}
print "  or \"q\" to quit:\n";

while (<STDIN>) {
	chomp;

        #Since it uses the same input line for all event types
        # here keep the sequence number,reason number. But in
        # adapter event and node event codes don't write them into
        # the socket since the new format of event file has no
        # those lines.
	my ($event, $event_type, $node_number, $node_id, $sequence_number, $arg1, $arg2) = split " ", $_;

	if ("q" eq $event) {
		last;
	}

	#
	# Silently fail if node_number not supplied, for any event except 'r'.
	#

	if (!$node_number) {
		#
		# Reconfigure doesn't require a node_number
		#
		if ("r" ne $event) {
			next;
		}
	}

	#
	# Reconfig only uses sequence_number and reason_number, so those
	# will be the first two parameters, not event_type & node_number.
	#
	if ("r" eq $event) {
		if ($event_type) {
			$sequence_number = $event_type
		}
		if ($node_number) {
			$arg1 = $node_number
		}
	}

	if (!$node_id) {
		$node_id = 1;
	}

	if (!$sequence_number) {
		$sequence_number = 1;
	}

	if ("u" eq $event_type) {
		$event_type = "UP";
	} elsif ("d" eq $event_type) {
		$event_type = "DOWN";
	} else {
		#
		# Reconfigure doesn't require an event_type.
		#
		if ("r" ne $event) {
			next;
		}
	}

	if (("a" eq $event) && $adapter_liveness_client) {
		if (!$arg1) {
			$arg1 = 0;
		}
		if (!$arg2) {
			$arg2 = "bogus_adapter";
		}
                &common_input( $adapter_liveness_client );
		print $adapter_liveness_client "EVENT_TYPE=ADAPTER_$event_type\n";
		print $adapter_liveness_client "NODE_NUMBER=$node_number\n";
		print $adapter_liveness_client "NODE_ID=$node_id\n";
		print $adapter_liveness_client "INTERFACE_NAME=$arg2\n";
		print $adapter_liveness_client "END_EVPROD_INFO\n";
		print $adapter_liveness_client "END_EVENT_INFO\n";
	} elsif (("n" eq $event) && $node_liveness_client) {
		if (!$arg1) {
			$arg1 = 0;
		}
                &common_input( $node_liveness_client );
	    print $node_liveness_client "EVENT_TYPE=NODE_$event_type\n";
	    print $node_liveness_client "NODE_NUMBER=$node_number\n";
	    print $node_liveness_client "NODE_ID=$node_id\n";
		print $node_liveness_client "END_EVPROD_INFO\n";
		print $node_liveness_client "END_EVENT_INFO\n";
	} elsif (("p" eq $event) && $process_liveness_client) {
		if (!$arg1) {
			$arg1 = "bogus_command";
		}
                &common_input( $process_liveness_client );
	    print $process_liveness_client "EVENT_TYPE=PROCESS_$event_type\n";
	    print $process_liveness_client "NODE_NUMBER=$node_number\n";
	    print $process_liveness_client "NODE_ID=$node_id\n";
	    print $process_liveness_client "COMMAND_NAME=$arg1\n";
		print $process_liveness_client "END_EVPROD_INFO\n";
		print $process_liveness_client "END_EVENT_INFO\n";
	} elsif (("r" eq $event) && $cluster_reconfig_client) {
		if (!$arg1) {
			$arg1 = 0;
		}
                &common_input( $cluster_reconfig_client );
		print $cluster_reconfig_client "EVENT_TYPE=CLUSTER_RECONFIG\n";
		print $cluster_reconfig_client "SEQUENCE_NUMBER=$sequence_number\n";
		print $cluster_reconfig_client "REASON_CODE=$arg1\n";
		print $cluster_reconfig_client "END_EVPROD_INFO\n";
		print $cluster_reconfig_client "END_EVENT_INFO\n";
	} else {
		next;
	}
}


cleanup();


exit 0; # end of main


sub remove_socket_files
{
	if ($adapter_liveness_path) {
		system("rm -f $adapter_liveness_path 1>/dev/null 2>/dev/null");
	}

	if ($node_liveness_path) {
		system("rm -f $node_liveness_path 1>/dev/null 2>/dev/null");
	}

	if ($process_liveness_path) {
		system("rm -f $process_liveness_path 1>/dev/null 2>/dev/null");
	}

	if ($cluster_reconfig_path) {
		system("rm -f $cluster_reconfig_path 1>/dev/null 2>/dev/null");
	}
}

sub cleanup
{
	if ($node_liveness_client) {
		$node_liveness_client->shutdown(2);
		$node_liveness_client->close();
	}

	if ($adapter_liveness_client) {
		$adapter_liveness_client->shutdown(2);
		$adapter_liveness_client->close();
	}

	if ($process_liveness_client) {
		$process_liveness_client->shutdown(2);
		$process_liveness_client->close();
	}

	if ($cluster_reconfig_client) {
		$cluster_reconfig_client->shutdown(2);
		$cluster_reconfig_client->close();
	}

	remove_socket_files();

	exit 0;
}

sub usage
{
	print STDERR <<USAGE;

Usage:
ahafs_sim_multi --adapter --node --process --reconfig
                [--help]

NOTE: At least one event type must be specified.

If the options are accepted, ahafs_sim_multi waits until a client connects to
each of the unix domain sockets corresponding to an event type. One all have
done so, ahafs_sim_multi goes into command mode, reading commands from standard
input. Aside from the 'q' (quit) command, commands have the following format:

event_category event_type node_number [node_id sequence_number reason_code|command_name interface_name]

where:

event_category
  a - adapter liveness
  n - node liveness
  p - process liveness
  r - cluster reconfig

event_type
  d - down
  u - up

Only the single character values are accepted (e.g. 'a', not "adapter").

"Cluster reconfig" is a special category which is not node-specific and does
not require any additional values (extra input will be ignored).

For all other categories, the first three values are required. If not
specified, or invalid values are given, the command silently fails.

The other values are optional. To specify an optional value, both it and all
values preceding it must be specified.

command_name applies only to a process liveness event.
interface_name applies only to an adapter liveness event.
USAGE

	if (undef ne $_[0])
	{
	exit $_[0];
	}
}