/*
 * Stonith: simple test program for exercising the Stonith API code
 *
 * Copyright (C) 2000 Alan Robertson <alanr@unix.sh>
 *
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <lha_internal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include <stonith/stonith.h>
#include <pils/plugin.h>
#include <glib.h>

#define	OPTIONS	"c:F:p:t:T:snSlLvhd"
#define	EQUAL	'='

extern char *	optarg;
extern int	optind, opterr, optopt;

static int	debug = 0;

void usage(const char * cmd, int exit_status, const char * devtype);
void confhelp(const char * cmd, FILE* stream, const char * devtype);

/*
 * Note that we don't use the cl_log logging code because the STONITH
 * command is intended to be shipped without the clplumbing libraries.
 *
 *	:-(
 */

void
usage(const char * cmd, int exit_status, const char * devtype)
{
	FILE *stream;

	stream = exit_status ? stderr : stdout;

	/* non-NULL devtype indicates help for specific device, so no usage */
	if (devtype == NULL) {
		fprintf(stream, "usage:\n");
		fprintf(stream, "\t %s [-svh] "
		"-L\n"
		, cmd);

		fprintf(stream, "\t %s [-svh] "
		"-t stonith-device-type "
		"-n\n"
		, cmd);

		fprintf(stream, "\t %s [-svh] "
		"-t stonith-device-type "
		"{-p stonith-device-parameters | "
		"-F stonith-device-parameters-file | "
		"name=value...} "
		"[-c count] "
		"-lS\n"
		, cmd);

		fprintf(stream, "\t %s [-svh] "
		"-t stonith-device-type "
		"{-p stonith-device-parameters | "
		"-F stonith-device-parameters-file | "
		"name=value...} "
		"[-c count] "
		"-T {reset|on|off} nodename\n"
		, cmd);

		fprintf(stream, "\nwhere:\n");
		fprintf(stream, "\t-L\tlist supported stonith device types\n");
		fprintf(stream, "\t-l\tlist hosts controlled by this stonith device\n");
		fprintf(stream, "\t-S\treport stonith device status\n");
		fprintf(stream, "\t-s\tsilent\n");
		fprintf(stream, "\t-v\tverbose\n");
		fprintf(stream, "\t-n\toutput the config names of stonith-device-parameters\n");
		fprintf(stream, "\t-h\tdisplay detailed help message with stonith device description(s)\n");
	}

	if (exit_status == 0) {
		confhelp(cmd, stream, devtype);
	}

	exit(exit_status);
}

/* Thanks to Lorn Kay <lorn_kay@hotmail.com> for the confhelp code */
void
confhelp(const char * cmd, FILE* stream, const char * devtype)
{
	char ** typelist;
	char ** this;
	Stonith *       s;
	int	devfound = 0;

	
	/* non-NULL devtype indicates help for specific device, so no header */
	if (devtype == NULL) {
		fprintf(stream
		,	"\nSTONITH -t device types and"
			" associated configuration details:\n");
	}

	typelist = stonith_types();
	
	if (typelist == NULL) {
		fprintf(stderr, 
			"Failed to retrieve list of STONITH modules!\n");
		return;
	}
	for(this=typelist; *this && !devfound; ++this) {
		const char *    SwitchType = *this;
		const char *	cres;
		const char **	pnames;


		if ((s = stonith_new(SwitchType)) == NULL) {
			fprintf(stderr, "Invalid STONITH type %s(!)\n"
			,	SwitchType);
			continue;
		}

		if (devtype) {
			if (strcmp(devtype, SwitchType)) {
				continue;
			} else {
				devfound = 1;
			}
		}

		fprintf(stream, "\n\nSTONITH Device: %s - ", SwitchType);

		if ((cres = stonith_get_info(s, ST_DEVICEDESCR)) != NULL){
			fprintf(stream, "%s\n"
			,	cres);
		}

		if ((cres = stonith_get_info(s, ST_DEVICEURL)) != NULL){
			fprintf(stream
			,	"For more information see %s\n"
			,	cres);
		}
		if (NULL == (pnames = stonith_get_confignames(s))) {
			continue;
		}
		fprintf(stream
		,	"List of valid parameter names for %s STONITH device:\n"
		,	SwitchType);
		for (;*pnames; ++pnames) {
			fprintf(stream
			,	"\t%s\n", *pnames);
		}

#ifdef ST_CONFI_INFO_SYNTAX
		fprintf(stream, "\nConfig info [-p] syntax for %s:\n\t%s\n"
		,    SwitchType, stonith_get_info(s, ST_CONF_INFO_SYNTAX));
#else
		fprintf(stream, "For Config info [-p] syntax"
		", give each of the above parameters in order as"
		"\nthe -p value.\n"
		"Arguments are separated by white space.");
#endif
#ifdef ST_CONFI_FILE_SYNTAX
		fprintf(stream, "\nConfig file [-F] syntax for %s:\n\t%s\n"
		,    SwitchType, stonith->get_info(s, ST_CONF_FILE_SYNTAX));
#else
		fprintf(stream
		,	"\nConfig file [-F] syntax is the same as -p"
		", except # at the start of a line"
		"\ndenotes a comment\n");
#endif

		stonith_delete(s); s = NULL;
	}
	/* Note that the type list can't/shouldn't be freed */
	if (devtype && !devfound) {
		fprintf(stderr, "Invalid device type: '%s'\n", devtype);
	}
	
}

#define	MAXNVARG	50

int
main(int argc, char** argv)
{
	char *		cmdname;
	int		rc;
	Stonith *	s;
	const char *	SwitchType = NULL;
	const char *	tmp;
	const char *	optfile = NULL;
	const char *	parameters = NULL;
	int		reset_type = ST_GENERIC_RESET;
	int		verbose = 0;
	int		status = 0;
	int		silent = 0;
	int		listhosts = 0;
	int		listtypes = 0;
	int 		listparanames = 0;

	int		c;
	int		errors = 0;
	int		argcount;
	StonithNVpair	nvargs[MAXNVARG];
	int		nvcount=0;
	int		j;
	int		count = 1;
	int		help = 0;

	if ((cmdname = strrchr(argv[0], '/')) == NULL) {
		cmdname = argv[0];
	}else{
		++cmdname;
	}


	while ((c = getopt(argc, argv, OPTIONS)) != -1) {
		switch(c) {

		case 'c':	count = atoi(optarg);
				if (count < 1) {
					fprintf(stderr
					,	"bad count [%s]\n"
					,	optarg);
					usage(cmdname, 1, NULL);
				}
				break;

		case 'd':	debug++;
				break;

		case 'F':	optfile = optarg;
				break;

		case 'h':	help++;
				break;

		case 'l':	++listhosts;
				break;

		case 'L':	++listtypes;
				break;

		case 'p':	parameters = optarg;
				break;

		case 's':	++silent;
				break;

		case 'S':	++status;
				break;

		case 't':	SwitchType = optarg;
				break;

		case 'T':	if (strcmp(optarg, "on")== 0) {
					reset_type = ST_POWERON;
				}else if (strcmp(optarg, "off")== 0) {
					reset_type = ST_POWEROFF;
				}else if (strcmp(optarg, "reset")== 0) {
					reset_type = ST_GENERIC_RESET;
				}else{
					fprintf(stderr
					,	"bad reset type [%s]\n"
					,	optarg);
					usage(cmdname, 1, NULL);
				}
				break;

		case 'n':	++listparanames;
				break;

		case 'v':	++verbose;
				break;

		default:	++errors;
				break;
		}
	}

	if (help && !errors) {
		usage(cmdname, 0, SwitchType);
	}
	if (debug) {
		PILpisysSetDebugLevel(debug);
	}
	if (optfile && parameters) {
		fprintf(stderr
		,	"Cannot include both -F and -p options\n");
		usage(cmdname, 1, NULL);
	}

	/*
	 *	Process name=value arguments on command line...
	 */
	for (;optind < argc; ++optind) {
		char *	eqpos;
		if ((eqpos=strchr(argv[optind], EQUAL)) == NULL) {
			break;
		}
		if (parameters)  {
			fprintf(stderr
			,	"Cannot include both -p and name=value "
			"style arguments\n");
			usage(cmdname, 1, NULL);
		}
		if (optfile)  {
			fprintf(stderr
			,	"Cannot include both -F and name=value "
			"style arguments\n");
			usage(cmdname, 1, NULL);
		}
		if (nvcount >= MAXNVARG) {
			fprintf(stderr
			,	"Too many name=value style arguments\n");
			exit(1);
		}
		nvargs[nvcount].s_name = argv[optind];
		*eqpos = EOS;
		nvargs[nvcount].s_value = eqpos+1;
		nvcount++;
	}
	nvargs[nvcount].s_name = NULL;
	nvargs[nvcount].s_value = NULL;

	argcount = argc - optind;

	if (!(argcount == 1 || (argcount < 1
	&&	(status||listhosts||listtypes||listparanames)))) {
		++errors;
	}

	if (errors) {
		usage(cmdname, 1, NULL);
	}

	if (listtypes) {
		char **	typelist;

		typelist = stonith_types();
		if (typelist == NULL) {
			syslog(LOG_ERR, "Could not list Stonith types.");
		}else{
			char **	this;

			for(this=typelist; *this; ++this) {
				printf("%s\n", *this);
			}
		}
		exit(0);
	}

	if (SwitchType == NULL) {
		fprintf(stderr,	"Must specify device type (-t option)\n");
		usage(cmdname, 1, NULL);
	}

	s = stonith_new(SwitchType);
	if (!listparanames && optfile == NULL && parameters == NULL && nvcount == 0) {
		const char**	names;
		int		needs_parms = 1;

		if (s != NULL && (names = stonith_get_confignames(s)) != NULL && names[0] == NULL) {
			needs_parms = 0;
		}

		if (needs_parms) {
			fprintf(stderr
			,	"Must specify either -p option, -F option or "
			"name=value style arguments\n");
			if (s != NULL) {
				stonith_delete(s); 
			}
			usage(cmdname, 1, NULL);
		}
	}

#ifndef LOG_PERROR
#	define LOG_PERROR	0
#endif
	openlog(cmdname, (LOG_CONS|(silent ? 0 : LOG_PERROR)), LOG_USER);
	if (s == NULL) {
		syslog(LOG_ERR, "Invalid device type: '%s'", SwitchType);
		exit(S_OOPS);
	}
	if (debug) {
		stonith_set_debug(s, debug);
	}

	if (listparanames) {
		const char**	names;
		int		i;
		names = stonith_get_confignames(s);

		if (names != NULL) {
			for (i=0; names[i]; ++i) {
				printf("%s  ", names[i]);
			}
		}
		printf("\n");
		stonith_delete(s); 
		s=NULL;
		exit(0);
	}

	/* Old STONITH version 1 stuff... */
	if (optfile) {
		/* Configure the Stonith object from a file */
		if ((rc=stonith_set_config_file(s, optfile)) != S_OK) {
			syslog(LOG_ERR
			,	"Invalid config file for %s device."
			,	SwitchType);
#if 0
			syslog(LOG_INFO, "Config file syntax: %s"
			,	s->s_ops->getinfo(s, ST_CONF_FILE_SYNTAX));
#endif
			stonith_delete(s); s=NULL;
			exit(S_BADCONFIG);
		}
	}else if (parameters) {
		/* Configure Stonith object from the -p argument */
		StonithNVpair *		pairs;
		if ((pairs = stonith1_compat_string_to_NVpair
		     (	s, parameters)) == NULL) {
			fprintf(stderr
			,	"Invalid STONITH -p parameter [%s]\n"
			,	parameters);
			stonith_delete(s); s=NULL;
			exit(1);
		}
		if ((rc = stonith_set_config(s, pairs)) != S_OK) {
			fprintf(stderr
			,	"Invalid config info for %s device"
			,	SwitchType);
		}
	}else{
		/*
		 *	Configure STONITH device using cmdline arguments...
		 */
		if ((rc = stonith_set_config(s, nvargs)) != S_OK) {
			const char**	names;
			int		j;
			fprintf(stderr
			,	"Invalid config info for %s device\n"
			,	SwitchType);

			names = stonith_get_confignames(s);

			if (names != NULL) {
				fprintf(stderr
				,	"Valid config names are:\n");
			
				for (j=0; names[j]; ++j) {
					fprintf(stderr
					,	"\t%s\n", names[j]);
				}
			}
			stonith_delete(s); s=NULL;
			exit(rc);
		}
	}


	for (j=0; j < count; ++j) {
		rc = stonith_get_status(s);

		if ((tmp = stonith_get_info(s, ST_DEVICEID)) == NULL) {
			SwitchType = tmp;
		}

		if (status && !silent) {
			if (rc == S_OK) {
				syslog(LOG_ERR, "%s device OK.", SwitchType);
			}else{
				/* Uh-Oh */
				syslog(LOG_ERR, "%s device not accessible."
				,	SwitchType);
			}
		}

		if (listhosts) {
			char **	hostlist;

			hostlist = stonith_get_hostlist(s);
			if (hostlist == NULL) {
				syslog(LOG_ERR, "Could not list hosts for %s."
				,	SwitchType);
			}else{
				char **	this;

				for(this=hostlist; *this; ++this) {
					printf("%s\n", *this);
				}
				stonith_free_hostlist(hostlist);
			}
		}

		if (optind < argc) {
			char *nodename;
			nodename = g_strdup(argv[optind]);
			g_strdown(nodename);
			rc = stonith_req_reset(s, reset_type, nodename);
			g_free(nodename);
		}
	}
	stonith_delete(s); s = NULL;
	return(rc);
}
