/* BEGIN_ICS_COPYRIGHT5 ****************************************

Copyright (c) 2015-2020, Intel Corporation

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of Intel Corporation nor the names of its contributors
      may be used to endorse or promote products derived from this software
      without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

** END_ICS_COPYRIGHT5   ****************************************/

//==============================================================================//
//										//
// FILE NAME									//
//    config_check.c								//
//										//
// DESCRIPTION									//
//    This file parses and verifies the opafm.xml config file //
//										//
// DATA STRUCTURES								//
//    None									//
//==============================================================================//


#include <getopt.h>

#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/limits.h>
#include "cs_g.h"
#include <fm_xml.h>

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE 1
#endif

#define MAX_INSTANCES 8
#define FM_CONFIG_FILENAME "/etc/opa-fm/opafm.xml"

uint8_t		debug;					// debugging mode - default is FALSE
uint8_t		checksum;				// checksum mode - default is FALSE
uint8_t		embedded;				// is it called internally ?- default is FALSE
char		config_file [PATH_MAX+1]; // location and name of the config file
char		reconfig_file [PATH_MAX+1]; // location and name of the reconfig file
Pool_t		startup_pool;			// a generic pool for malloc'ing memory
#define		PROG_NAME_MAX 25
char 		prog_name[PROG_NAME_MAX+1];

int instance = 0;

// XML configuration data structure
FMXmlCompositeConfig_t *xml_config = NULL;
FMXmlCompositeConfig_t *xml_reconfig = NULL;
FMXmlConfig_t *fmp;
SMXmlConfig_t *smp;
PMXmlConfig_t *pmp;
FEXmlConfig_t *fep;

void		Usage		(void);
Status_t	alloc_mem		(void);
void		free_mem		(void);
void		check_multicast_VFs (int num_instances, VirtualFabrics_t **vf_configs);
void		print_startup_information		(void);
void		print_checksum_information (int num_instances, VirtualFabrics_t **vf_configs);
void* getXmlParserMemory(uint32_t size, char* info);
void freeXmlParserMemory(void *address, uint32_t size, char* info);

//command line options
struct option options[]={
	{"help", no_argument, NULL, '$'},
	{0}
};

void
Usage (void)
{
	printf ("Usage %s [-s] [-c config_file] [-v] [-d] [-r reconfig_file]\n", prog_name);
	printf ("        or %s --help\n",prog_name);
	printf ("\n");
	printf ("   -c config file    (default=" FM_CONFIG_FILENAME ")\n");
	printf ("   -v                display debugging and status information\n");
	printf ("   -s                strict check mode (validate multicast and VF settings)\n");
	printf ("                     This option will point out inconsistencies or\n"); 
	printf ("                     invalid settings in VF and multicast config.\n");
	printf ("   -d                display configuration checksum information\n");
	printf ("   -r reconfig_file  verify that reconfig_file is valid for dynamical reconfiguration\n");
	printf ("\n");
	printf ("Examples:\n");
	printf ("  %s\n", prog_name);
	printf ("  %s -v\n", prog_name);
	printf ("  %s -sv\n", prog_name);
	// Note - the -e option is not presented to user since it is for embedded use only
	exit(2);
}


void
Usage_full (void)
{
	printf ("Usage %s [-s] [-c config_file] [-v] [-d] [-r reconfig_file]\n", prog_name);
	printf ("        or %s --help\n",prog_name);
	printf ("\n");
	printf ("   -c config file    (default=" FM_CONFIG_FILENAME ")\n");
	printf ("   -v                display debugging and status information\n");
	printf ("   -s                strict check mode (validate multicast and VF settings)\n");
	printf ("                     This is option will point out inconsistencies or\n"); 
	printf ("                     invalid settings in VF and multicast config.\n");
	printf ("   -d                display configuration checksum information\n");
	printf ("   -r reconfig_file  verify that reconfig_file is valid for dynamical reconfiguration\n");
	printf ("\n");
	printf (" %s parses and verifies the configuration file of a FM.\n", prog_name);
	printf ("Displays debugging and status information.\n");
	printf ("\n");
	printf ("Examples:\n");
	printf ("  %s\n", prog_name);
	printf ("  %s -v\n", prog_name);
	printf ("  %s -sv\n", prog_name);
	// Note - the -e option is not presented to user since it is for embedded use only
	exit(0);
}

static void print_warn(const char *msg)
{
	fprintf(stdout, "FM Instance %d: WARNING: %s\n",instance,msg);
}

static void print_error(const char *msg)
{
	fprintf(stdout, "FM Instance %d: ERROR: %s\n",instance,msg);
}

int
main (int argc, char *argv []) {
	int		c;		// used to parse the command line
	char 		tmp[PATH_MAX+1];
	char *		ptr;
	int		index;
	int ret = 0;
	IXmlParserFlags_t parser_flags = IXML_PARSER_FLAG_NONE;
	
	debug       = FALSE;
	checksum    = FALSE;
	embedded    = FALSE;
	strncpy (config_file, FM_CONFIG_FILENAME, PATH_MAX);
	strncpy (reconfig_file, "", PATH_MAX);
	strncpy (tmp, argv[0], PATH_MAX);
	tmp[PATH_MAX] = 0;
	ptr = strrchr(tmp, '/');
	strncpy (prog_name, ptr ? ptr+1 : tmp, PROG_NAME_MAX);
	prog_name[PROG_NAME_MAX]=0;
	
	VirtualFabrics_t *vf_configs[MAX_INSTANCES] = { NULL };
	VirtualFabrics_t *vf_reconfigs[MAX_INSTANCES] = { NULL };

	while ((c = getopt_long (argc, argv, "c:vsder:", options, &index)) != -1) {
		switch (c) {
		case '$':
			Usage_full ();
			break;
		// input config file
		case 'c':
			StringCopy(config_file, optarg, sizeof(config_file));
			break;
		case 'v':
			debug = TRUE;
			break;
		case 's':
			parser_flags = IXML_PARSER_FLAG_STRICT;
			break;
		case 'd':
			checksum = TRUE;
			break;
		case 'e':
			embedded = TRUE;
			break;
		case 'r':
			StringCopy(reconfig_file, optarg, sizeof(reconfig_file));
			break;
		default:
			fprintf(stderr, "invalid argument -%c\n", c);
			Usage ();
		}
	}
	if (optind < argc) {
		Usage ();
	}
	
	// Allocate memory for reading and parsing the config file.
	if (alloc_mem () != VSTATUS_OK) {
		exit(1);
	}

	//if (read_info_file() != VSTATUS_OK) {
	//	exit(1);
	//}

	// init callback function for XML parser so it can get pool memory
	initXmlPoolGetCallback(&getXmlParserMemory);
	initXmlPoolFreeCallback(&freeXmlParserMemory);

	// parse the XML config file
	xml_config = parseFmConfig(config_file, parser_flags, /* instance does not matter for startup */ 0, /* full */ 1, /* preverify */ 0, /* embedded */ embedded);
	if (!xml_config) {
		fprintf(stdout, "Could not open or there was a parse error while reading configuration file ('%s')\n", config_file);
		ret = 1;
		goto failed;
	}

	for (instance = 0; instance < xml_config->num_instances; instance++) {
		vf_configs[instance] = renderVirtualFabricsConfig(instance, xml_config, print_error, print_warn);
		if (vf_configs[instance] == NULL) {
			fprintf(stdout, "FM instance %d of configuration file ('%s) is invalid.\n",instance,config_file);
			ret = 1;
			goto failed;
		}
	}

	if (parser_flags & IXML_PARSER_FLAG_STRICT) {
		check_multicast_VFs(xml_config->num_instances, vf_configs);
	}

	if (debug) {
		fprintf(stdout, "Successfully parsed %s\n", config_file);
		print_startup_information();
	}

	if (checksum)
		print_checksum_information(xml_config->num_instances, vf_configs);

	// parse the new XML config file, if specified
	if (strlen(reconfig_file)) {
		xml_reconfig = parseFmConfig(reconfig_file, parser_flags, 0, /* full */ 1, /* preverify */ 0, /* embedded */ embedded);
		if (!xml_reconfig) {
			fprintf(stdout, "Could not open or there was a parse error while reading configuration file ('%s')\n", reconfig_file);
			ret = 1;
			goto failed;
		}

		for (instance = 0; instance < xml_config->num_instances; instance++) {
			vf_reconfigs[instance] = reRenderVirtualFabricsConfig(instance, vf_configs[instance], xml_reconfig, print_error, print_warn);
			if (vf_reconfigs[instance] == NULL) {
				fprintf(stdout, "Reconfiguration to file ('%s') for instance %d is invalid\n",reconfig_file,instance);
				ret = 1;
				goto failed;
			}
		}
	}

failed:
	// if we have XML configs then release them
	if (xml_reconfig != NULL) {
		for (instance = 0; instance < xml_reconfig->num_instances; instance++) {
			if (vf_reconfigs[instance]) {
				releaseVirtualFabricsConfig(vf_reconfigs[instance]);
				vf_reconfigs[instance] = NULL;
			}
		}
		releaseXmlConfig(xml_reconfig, /* full */ 1);
		xml_config = NULL;
	}
	if (xml_config != NULL) {
		for (instance = 0; instance < xml_config->num_instances; instance++) {
			if (vf_configs[instance]) {
				releaseVirtualFabricsConfig(vf_configs[instance]);
				vf_configs[instance] = NULL;
			}
		}
		releaseXmlConfig(xml_config, /* full */ 1);
		xml_config = NULL;
	}

	//	Delete a pool of memory to work with.
	free_mem ();

	exit(ret);
}

// get memory for XML parser
void*
getXmlParserMemory(uint32_t size, char* info) {
	void        *address;
	Status_t    status;

#ifdef XML_MEMORY
	printf("called getXmlParserMemory() size (%u) (%s) from config_check.c\n", size, info);
#endif
	status = vs_pool_alloc(&startup_pool, size, (void*)&address);
	if (status != VSTATUS_OK || !address)
		return NULL;
	return address;
}

// free memory for XML parser
void
freeXmlParserMemory(void *address, uint32_t size, char* info) {

#ifdef XML_MEMORY
	printf("called freeXmlParserMemory() size (%u) (%s) from config_check.c\n", size, info);
#endif
	vs_pool_free(&startup_pool, address);
}

//------------------------------------------------------------------------------//
//										//
// Routine      : alloc_mem							//
//										//
// Description  : Allocate memory.						//
// Input        : None								//
// Output       : Status of the startup process					//
//                  0 - memory was allocated					//
//                  1 - error allocating memory					//
//										//
//------------------------------------------------------------------------------//

Status_t
alloc_mem (void) {
	uint32_t	pool_size;
	Status_t	status;
	
//
//	Create a pool of memory to work with.
//
	
	// calculate pool size for all instances since this is the initial parsing
	// check for all instances of FM and VF configuration.
	pool_size = xml_compute_pool_size(/* for all instances of sm */ 1);
	status = vs_pool_create (&startup_pool, 0, (uint8_t *)"startup_pool", NULL, pool_size);
	if (status != VSTATUS_OK) {
		fprintf ( stderr, "Could not create a pool (status = %d)\n", status);
		return (VSTATUS_BAD);
	}
	
	return (VSTATUS_OK);
}


//------------------------------------------------------------------------------//
//										//
// Routine      : free_mem							//
//										//
// Description  : Free up allocated memory.					//
// Input        : None								//
// Output       : None								//
//										//
//------------------------------------------------------------------------------//

void
free_mem (void) {
	
	(void) vs_pool_delete (&startup_pool);
	
	return;
}

//------------------------------------------------------------------------------//
//										//
// Routine      : check_multicast_VFs						//
//										//
// Description  : do strict checking of consistency of multicast and VFs
// Input        : None								//
// Output       : None								//
//										//
//------------------------------------------------------------------------------//

void
check_multicast_VFs (int num_instances, VirtualFabrics_t **vf_configs) {
	uint16_t	i;

	for (i = 0; i < num_instances; i++)
	{
		FMXmlInstance_t *fmip = xml_config->fm_instance[i];
		smp = &xml_config->fm_instance[i]->sm_config;
		uint32_t j;

		fmp = &fmip->fm_config;;

		// if not starting instance of FM then continue to next FM instance
		if (!fmp->start)
			continue;

		for (j=0; j< fmip->sm_mdg_config.number_of_groups; ++j) {
			SMMcastDefGrp_t *mdgp = &fmip->sm_mdg_config.group[j];
			if (! mdgp->def_mc_create)
				continue;
			if (strlen(mdgp->virtual_fabric)) {
				if (! findVfPointer(vf_configs[i], mdgp->virtual_fabric)) {
					fprintf(stdout,"Warning: FM instance %u (%s) MulticastGroup Ignored\n    references undefined VirtualFabric: %s\n",
				   	i, fmp->fm_name, mdgp->virtual_fabric);
				}
			}
			if (mdgp->def_mc_pkey != 0xffffffff) {
				uint32_t vf;
				int match = 0;
				for (vf=0; vf < vf_configs[i]->number_of_vfs_all; vf++) {
					if (vf_configs[i]->v_fabric_all[vf].standby) continue;
					if (PKEY_VALUE(vf_configs[i]->v_fabric_all[vf].pkey) == PKEY_VALUE(mdgp->def_mc_pkey)) {
						match=1;
						break;
					}
				}
				if (! match) {
					fprintf(stdout,"Warning: FM instance %u (%s) MulticastGroup Ignored\n    references undefined VirtualFabric PKey: 0x%x\n",
				   	i, fmp->fm_name, mdgp->def_mc_pkey);
				}
			}
		}
	}
}

//------------------------------------------------------------------------------//
//										//
// Routine      : print_startup_information						//
//										//
// Description  : Add a line of startup information to our data structures.		//
// Input        : None								//
// Output       : None								//
//										//
//------------------------------------------------------------------------------//

void
print_startup_information (void) {
	uint16_t	i;

	for (i = 0; i < MAX_INSTANCES; i++)
	{
		if (!xml_config->fm_instance[i]) continue;
		fmp = &xml_config->fm_instance[i]->fm_config;
		smp = &xml_config->fm_instance[i]->sm_config;
		pmp = &xml_config->fm_instance[i]->pm_config;
		fep = &xml_config->fm_instance[i]->fe_config;

		// if none of these are set, then it was not in config file
		if (! fmp->start
		  	&& fmp->hca == 0xffffffff && fmp->port == 0xffffffff
			&& fmp->port_guid == 0xffffffffffffffffULL)
			continue;

		fprintf(stdout,"FM instance %u (%s) will%s be started\n",
					   i, fmp->fm_name, fmp->start?"":" not");

		// if not starting instance of FM then continue to next FM instance
		if (!fmp->start)
			continue;

		if (fmp->port_guid) {
			fprintf(stdout, "   PortGuid: 0x%016"PRIx64"\n", fmp->port_guid);
		} else {
			fprintf(stdout, "   HFI: %u Port: %u\n", fmp->hca, fmp->port);
		}

		fprintf(stdout, "   SM will%s be started\n", 
				smp->start?"":" not");
	
		//fprintf(stdout, "   PM will%s be started\n", 
		//		pmp->start?"":" not");
	
		fprintf(stdout, "   FE will%s be started\n", 
				fep->start?"":" not");
	}
}

//------------------------------------------------------------------------------//
//										//
// Routine      : print_checksum_information						//
//										//
// Description  : Prints checksums of each parsed XML component//
// Input        : None								//
// Output       : None								//
//										//
//------------------------------------------------------------------------------//

void
print_checksum_information (int num_instances, VirtualFabrics_t **vf_configs) {
	uint16_t	i;
	uint16_t	v;

	fprintf(stdout,"\n");
	for (i = 0; i < num_instances; i++) {

		fmp = &xml_config->fm_instance[i]->fm_config;;
		smp = &xml_config->fm_instance[i]->sm_config;
		pmp = &xml_config->fm_instance[i]->pm_config;
		fep = &xml_config->fm_instance[i]->fe_config;

		fprintf(stdout,"FM instance %u\n", i);
		fprintf(stdout, "   SM overall checksum %8u    consistency checksum %8u\n", smp->overall_checksum, smp->consistency_checksum);
		fprintf(stdout, "   PM overall checksum %8u    consistency checksum %8u\n", pmp->overall_checksum, pmp->consistency_checksum);
		fprintf(stdout, "   VF database consistency checksum %8u\n", vf_configs[i]->consistency_checksum);
		for (v = 0; v < vf_configs[i]->number_of_vfs_all; v++) {
			if (vf_configs[i]->v_fabric_all[v].standby) continue;
			fprintf(stdout, "       VF %s consistency checksum %8u\n", vf_configs[i]->v_fabric_all[v].name,
				vf_configs[i]->v_fabric_all[v].consistency_checksum);
		}
		fprintf(stdout,"\n");
	}
}
