#! /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 

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

my $prompt = "=== terminal input mode (q|quit|<[file]|{|}|value <enter>)";

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

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


#
# Only one predefined template can be loaded.
#

if ($adapter + $node + $process + $reconfig > 1)
{
	usage(1);
}


my $path;

if ($adapter) {
	$path = "/tmp/Cluster/{adapter_events";
} elsif ($node) {
	$path = "/tmp/Cluster/{node_events";
} elsif ($process) {
	$path = "/tmp/Cluster/{process_events";
} elsif ($reconfig) {
	$path = "/tmp/Cluster/{reconfig_events";
} else {
	#
	# At least one predefined template must be specified.
	#

	usage(1);
}

#
# 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_file();

#
# Cleanup socket file on Ctrl-C.
#

$SIG{INT} = \&cleanup;



STDOUT->autoflush(1);


#
# Create the unix domain sockets for the specified path.
#

my $socket;

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

$socket->autoflush(1);


#
# Wait for a client on the socket.
#

my $client;
my $broken_pipe;

accept_client();

$broken_pipe = 0;

#
# Trap the SIGPIPE signal delivered to the process when attempting to write to
# a broken pipe.
#

$SIG{PIPE} = \&accept_client;


my @template;

if (!$no_template) {
        push @template, "BEGIN_EVENT_INFO";
        push @template, "RC_FROM_EVPROD=0";
        push @template, "SEQUENCE_NUM=0";
        push @template, "TIME_tvsec=1244830365"; 
        push @template, "TIME_tvnsec=637528480"; 
        push @template, "PID=360544"; 
        push @template, "UID=0"; 
        push @template, "UID_LOGIN=0"; 
        push @template, "GID=0"; 
        push @template, "PROG_NAME=ahafs_sim_multi";
	push @template, "BEGIN_EVPROD_INFO";

	if ($adapter) {
		push @template, "EVENT_TYPE=ADAPTER_%s";
		push @template, "NODE_NUMBER=%s";
		push @template, "NODE_ID=%s";
		push @template, "INTERFACE_NAME=%s";
	} elsif ($node) {
		push @template, "EVENT_TYPE=NODE_%s";
		push @template, "NODE_NUMBER=%s";
		push @template, "NODE_ID=%s";
	} elsif ($process) {
		push @template, "EVENT_TYPE=PROCESS_%s";
		push @template, "NODE_NUMBER=%s";
		push @template, "NODE_ID=%s";
		push @template, "SEQUENCE_NUMBER=%s";
		push @template, "COMMAND_NAME=%s";
	} elsif ($reconfig) {
		push @template, "EVENT_TYPE=CLUSTER_RECONFIG";
		push @template, "SEQUENCE_NUMBER=%s";
		push @template, "REASON_CODE=%s";
	}
	push @template, "END_EVPROD_INFO";
        push @template, "END_EVENT_INFO";
}

#
# Read commands form STDIN.
#

while (<STDIN>) {
	chomp;

	if (/^\s*#/) {
		next;
	} elsif (/{/) {
		@template = ();

		while (<STDIN>) {
			chomp;

			if (/^\s*#/) {
				next;
			} elsif (/}/) {
				last;
			}

			push(@template, $_);
		}
	} elsif (/^quit/ || /^q$/) {
		last;
	} elsif (/^</) {
		close STDIN;

		if (/^<$/) {
			open STDIN, "/dev/tty" or die "couldn't reopen STDIN to /dev/tty\n";
			print "$prompt\n";
		} else {
			my ($token, $file) = split " ";

			if (! $file) {
				$file = substr($token, 1);
			}

			open STDIN, "<$file" or die "couldn't reopen STDIN to $file\n";
		}
	} else {

		#
		# Split the input line into supplied values.
		#

		my @values = split " ";

		#
		# For each template entry, if it contains an '%s', output with the
		# next supplied value. Otherwise, output as is.

		foreach my $template_entry (@template) {

			my $value = "";

			if ($template_entry =~ /%s/ )
			{
				$value = shift(@values);
	
				if (! $value) {
					$value = "1";
				}
			}

			my $output_string = sprintf("$template_entry\n", $value);
	
			print $client $output_string;
	
			if ($broken_pipe) {
				print $client $output_string;
				$broken_pipe = 0;
			}
		}
	}
}


cleanup();


exit 0; # end of main


sub accept_client {

	#
	# Block until a client connects.
	#

	$client = $socket->accept();
	print "client has connected\n";
	
	#
	# Verify the client is requesting the right stuff.
	#
	
	my $expected_request = "CHANGED=YES;CLUSTER=YES";
	
	if ($client) {
		my $actual_request = <$client>;
	
		chomp $actual_request;
	
		if ($actual_request ne $expected_request) {
			print "ERROR: client: expected_request($expected_request) actual_request($actual_request)\n";
			exit 1;
		}
	}

	#
	# Say pipe is broken. Caller can override as necessary.
	#

	$broken_pipe = 1;
}


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

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

	remove_socket_file();

	exit(0);
}

# For input we still keep the sequence number, reason number
# but in writing to socket don't write them since the input
# code is also used for process event,reconfiguration event
# But for node event and adapter event they have no sequence
# number or reason code in parsing any more. 

sub usage
{
	print STDERR <<"USAGE";

Usage:
ahafs_sim --adapter | --node | --process | --reconfig
          [--no_template]
          [--help]

where:

--adapter     - pre-loads the adapter liveness template
--node        - pre-loads the node liveness template
--process     - pre-loads the process liveness template
--reconfig    - pre-loads the cluster reconfiguration template
--no_template - permits socket file association per the selected
                template, but doesn't actually load the template

If the options are accepted, ahafs_sim waits until a client connects to the
unix domain socket associated with a given liveness template type, after which
it goes into command mode, reading commands from standard input. Acceptable
commands include:

#     - comment if first character in line
{     - begin template definition (must appear alone on line)
}     - end template definition (must appear alone on line)
<     - begin terminal input mode
<file - enter commands from file
q     - quit
quit  - quit

Between '{' and '}', commands are considered part of the template definition.
Template definition lines containing a '%s' will be completed by value input
lines. Only the first %s in a line will be substituted. (More than one %s in
a line may cause program malfunction.)

Anything other than the above is considered a value input line, containing
values to be substituted in the current template.

Values in the template not specified in a value input lines are defaulted to
'1'.

Examples:

1) Begin a node simulator. Commands are input from the terminal.

> ./ahafs_sim --node
$prompt
UP 1 2 3 4

A client would receive the following:
BEGIN_EVENT_INFO
RC_FROM_EVPROD=0
SEQUENCE_NUM=0
TIME_tvsec=1244830365 
TIME_tvnsec=637528480 
PID=360544 
UID=0 
UID_LOGIN=0 
GID=0 
PROG_NAME=<name>
BEGIN_EVPROD_INFO
EVENT_TYPE=NODE_UP
NODE_NUMBER=1
NODE_ID=2
SEQUENCE_NUMBER=3
REASON_CODE=4
END_EVPROD_INFO
END_EVENT_INFO


2) Begin a node simulator whose template definition is supplied manually.
   Commands are input from the terminal.

> ./ahafs_sim --node --no_template
$prompt
{
BEGIN_EVENT_INFO
RC_FROM_EVPROD=0
SEQUENCE_NUM=0
TIME_tvsec=1244830365 
TIME_tvnsec=637528480 
PID=360544 
UID=0 
UID_LOGIN=0 
GID=0 
PROG_NAME=<name>
BEGIN_EVPROD_INFO
EVENT_TYPE=NODE_%s
NODE_NUMBER=%s
NODE_ID=%s
SEQUENCE_NUMBER=%s
REASON_CODE=%s
END_EVPROD_INFO
END_EVENT_INFO
}
UP 1 2 3 4

A client would receive the following:
BEGIN_EVENT_INFO
RC_FROM_EVPROD=0
SEQUENCE_NUM=0
TIME_tvsec=1244830365 
TIME_tvnsec=637528480 
PID=360544 
UID=0 
UID_LOGIN=0 
GID=0 
PROG_NAME=<name>
BEGIN_EVPROD_INFO
EVENT_TYPE = NODE_UP
NODE_NUMBER=1
NODE_ID=2
SEQUENCE_NUMBER=3
REASON_CODE=4
END_EVPROD_INFO
END_EVENT_INFO

3) Begin an adapter simulator whose commands are input from a file.

> ./ahafs_sim --adapter <INPUT_FILE

NOTE: The INPUT_FILE should end with a "<" command if you want the
simulator to enter terminal input mode after the file is processed.


USAGE

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