#!/usr/bin/perl
#
# crypto_opencryptoki_ckc
#   Health check program for the Linux Health Checker
#
# Copyright IBM Corp. 2012
#
# Author(s): Nageswara R Sastry <nasastry@in.ibm.com>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# Contributors: See file CONTRIBUTORS which is part of this package
#

use strict;
use warnings;

use lib $ENV{"LNXHC_LIBDIR"};
use LNXHC::Check::Base;
use LNXHC::Check::Util qw/:proc/;

#
# Global variables
#
my $linux_distro = $ENV{"LNXHC_SYS_sys_distro"};

# Exception IDs
my $LNXHC_EXCEPTION_CRYPTO_ADAPTERS_NOT_ONLINE = "crypto_adapters_not_available";
my $LNXHC_EXCEPTION_32BIT_RPMS_NOT_INSTALLED = "32bit_rpms_not_installed";
my $LNXHC_EXCEPTION_ICA_TOKEN_NOT_CONFIGURED = "ica_token_not_configured";
my $LNXHC_EXCEPTION_OPENCRYPTOKI_NOT_INITIALIZED = "opencryptoki_not_initialized";

# Path to the file containing data for sysinfo item 'pkcsconf'.
my $sysinfo_pkcsconf = $ENV{"LNXHC_SYSINFO_pkcsconf"};

# Path to the file containing data for sysinfo item 'rpm_query_all'.
my $sysinfo_rpm_query_all = $ENV{"LNXHC_SYSINFO_rpm_query_all"};

# Path to the file containing data for sysinfo item 'proc_cpuinfo'
my $sysinfo_proc_cpuinfo = $ENV{"LNXHC_SYSINFO_proc_cpuinfo"};

# Path to the file containing data for sysinfo item 'dev_node'.
my $sysinfo_dev_node = $ENV{"LNXHC_SYSINFO_dev_node"};

# Path to the file containing data for sysinfo item 'hw_info'.
my $sysinfo_hw_info = $ENV{"LNXHC_SYSINFO_hw_info"};

# Return code for sysinfo item 'dev_node'
my $rc_dev_node = $ENV{"LNXHC_SYSINFO_EXIT_CODE_dev_node"};

# Return code for sysinfo item 'sysinfo_pkcsconf'
my $rc_pkcsconf = $ENV{"LNXHC_SYSINFO_EXIT_CODE_pkcsconf"};

# ICA Flag for configured
my $ica_config_flag = "44D";

my $handle;


# Defining the RPM names required.
my $opencryptoki_rpm = "opencryptoki";
my $opencryptoki_libs = "opencryptoki-libs";
my $libica_rpm = "libica";

# Checking for CPACF availability
my $cpuinfo = load_proc_cpuinfo($sysinfo_proc_cpuinfo);
die "Failed to read sysinfo: $sysinfo_proc_cpuinfo: $!\n" unless $cpuinfo;

my @features = split /\s+/, $cpuinfo->{features};
unless (grep { /\bmsa\b/ } @features) {
	lnxhc_fail_dep("The CP Assist for Cryptographic Functions (CPACF) is ".
		       "not available");
}

# Checking required RPMs installed or not
open ($handle, "<", $sysinfo_rpm_query_all) or
die("Couldn't open file: $sysinfo_rpm_query_all: $!\n");

my $s390x = "\.s390x";
my $s390 = "\.s390";
my @rpm_query_file = <$handle>;
close ($handle);
my (@opencryptoki, @libica);
if ($linux_distro eq "RHEL") {
	@opencryptoki = grep { /^$opencryptoki_rpm-\d+.*$s390$|^$opencryptoki_libs-\d+.*$s390$/i } @rpm_query_file;
	@libica = grep { /^$libica_rpm-\d+\..*$s390$/ } @rpm_query_file;
}
if ($linux_distro eq "SLES") {
	@opencryptoki = grep { /^$opencryptoki_rpm-32bit.\d+.*$s390$/i } @rpm_query_file;
	@libica = grep { /^$libica_rpm-\d+[\._\d]*-32bit.*$s390x$/ } @rpm_query_file;
}

# Variable used for displaying which RPMs are missing.
my @rpm_unavail;
push @rpm_unavail, $opencryptoki_rpm unless (@opencryptoki);
push @rpm_unavail, $libica_rpm unless (@libica);

if (@rpm_unavail)  {
	lnxhc_exception($LNXHC_EXCEPTION_32BIT_RPMS_NOT_INSTALLED);
	lnxhc_exception_var_list("rpm", \@rpm_unavail, ", ", scalar(@rpm_unavail));
	lnxhc_exception_var_list("rpm_summ", \@rpm_unavail, ", ");
	# No need to check any further
	exit(0);
}

if ($rc_pkcsconf != 0) {
	lnxhc_exception($LNXHC_EXCEPTION_OPENCRYPTOKI_NOT_INITIALIZED);
	# No need to check any further
	exit(0);
}

# Verifying ibmca engine is configured with openssl or not
open ($handle, "<", $sysinfo_pkcsconf) or
die("Couldn't open file: $sysinfo_pkcsconf: $!\n");
my $ica_data;
{
	local $/;
	$ica_data = <$handle>;
}
my $ica_flag = "";
close($handle);
# Looking for ICA and collecting it's flags
if ($ica_data =~ /^Token #\s*.*?Model: IBM ICA.*?Flags: 0x([[:xdigit:]]+)\s/smg) {
	$ica_flag = $1;
}
if ($ica_flag ne $ica_config_flag) {
	lnxhc_exception($LNXHC_EXCEPTION_ICA_TOKEN_NOT_CONFIGURED);
	# No need to check any further
	exit(0);
}

if ($rc_dev_node == 0) {
	# Checking required hardware available or not
	my $handle;
	open ($handle, "<", $sysinfo_hw_info) or
	die("Couldn't open file: $sysinfo_hw_info: $!\n");

	my ($hw_cop, $hw_acc);
	while (<$handle>) {
		next unless /^card\d+: CEX/;
		if (/^card\d+: CEX\d+C/) {
			$hw_cop = 1;
		}
		if (/^card\d+: CEX\d+A/) {
			$hw_acc = 1;
		}
	}
	close($handle);

	my @hw_unavail;
	# Pushing the name of the hardware, which is not available
	push @hw_unavail, "Cryptographic Coprocessor" unless ($hw_cop);
	push @hw_unavail, "Cryptographic Accelerator" unless ($hw_acc);
	# Raise the exception when both the cards are not available
	if (scalar(@hw_unavail) > 1) {
		lnxhc_exception($LNXHC_EXCEPTION_CRYPTO_ADAPTERS_NOT_ONLINE);
		lnxhc_exception_var_list("crypto_hw", \@hw_unavail, ", ");
	}
}

exit(0);
