/* $Id: drac3.c,v 1.3.2.1 2004/04/20 08:50:06 alan Exp $ */
/*
 * Stonith module for Dell DRACIII (Dell Remote Access Card)
 *
 * Copyright (C) 2003 Alfa21 Outsourcing
 * Copyright (C) 2003 Roberto Moreda <moreda@alfa21.com>
 *
 * (Using snippets of other stonith modules code)
 *
 * 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 <portability.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <libintl.h>
#include <sys/wait.h>
#include <glib.h>

#include <curl/curl.h>
#include "drac3_command.h"

#include <stonith/stonith.h>
#define PIL_PLUGINTYPE          STONITH_TYPE
#define PIL_PLUGINTYPE_S        STONITH_TYPE_S
#define PIL_PLUGIN              drac3
#define PIL_PLUGIN_S            "drac3"
#define PIL_PLUGINLICENSE       LICENSE_LGPL
#define PIL_PLUGINLICENSEURL    URL_LGPL
#include <pils/plugin.h>
#include "stonith_signal.h"

static void
drac3closepi(PILPlugin *pi)
{
}

static PIL_rc
drac3closeintf(PILInterface *pi, void *pd)
{
	return PIL_OK;
}

static void *	drac3_new(void);
static void		drac3_destroy(Stonith *);
static int		drac3_set_config_file(Stonith *, const char * cfgname);
static int		drac3_set_config_info(Stonith *, const char * info);
static const char * drac3_getinfo(Stonith * s, int InfoType);
static int		drac3_status(Stonith * );
static int		drac3_reset_req(Stonith * s, int request, const char * host);
static char **	drac3_hostlist(Stonith  *);
static void		drac3_free_hostlist(char **);

static struct stonith_ops drac3Ops ={
	drac3_new,		/* Create new STONITH object	*/
	drac3_destroy,		/* Destroy STONITH object	*/
	drac3_set_config_file,	/* set configuration from file	*/
	drac3_set_config_info,	/* Get configuration from file	*/
	drac3_getinfo,		/* Return STONITH info string	*/
	drac3_status,			/* Return STONITH device status	*/
	drac3_reset_req,		/* Request a reset */
	drac3_hostlist,		/* Return list of supported hosts */
	drac3_free_hostlist		/* free above list */
};

PIL_PLUGIN_BOILERPLATE("1.0", Debug, drac3closepi);
static const PILPluginImports*  PluginImports;
static PILPlugin*               OurPlugin;
static PILInterface*		OurInterface;
static StonithImports*		OurImports;
static void*			interfprivate;

#define LOG		PluginImports->log
#define MALLOC		PluginImports->alloc
#define STRDUP  	PluginImports->mstrdup
#define FREE		PluginImports->mfree
#define EXPECT_TOK	OurImports->ExpectToken
#define STARTPROC	OurImports->StartProcess

PIL_rc
PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);

PIL_rc
PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
{
	/* Force the compiler to do a little type checking */
	(void)(PILPluginInitFun)PIL_PLUGIN_INIT;

	PluginImports = imports;
	OurPlugin = us;

	/* Register ourself as a plugin */
	imports->register_plugin(us, &OurPIExports);  

	/*  Register our interface implementation */
 	return imports->register_interface(us, PIL_PLUGINTYPE_S
	,	PIL_PLUGIN_S
	,	&drac3Ops
	,	drac3closeintf		/*close */
	,	&OurInterface
	,	(void*)&OurImports
	,	&interfprivate); 
}

#define DEVICE  "Dell DRAC III Card"
#define BUFLEN	1024

#define _(text) dgettext(ST_TEXTDOMAIN, text)

struct DRAC3Device {
	const char *DRAC3id;
	CURL *curl;
	char *host;
	char *user;
	char *pass;
};

static const char *DRAC3id = DEVICE;
static const char *NOTdrac3ID = "destroyed (Dell DRAC III Card)";

#define ISDRAC3DEV(i) (((i)!= NULL && (i)->pinfo != NULL) && \
		                    ((struct DRAC3Device *)(i->pinfo))->DRAC3id == DRAC3id)

#define ISCONFIGED(i) (((struct DRAC3Device *)(i->pinfo))->curl != NULL )


#ifndef MALLOCT
#  define MALLOCT(t) ((t *)(MALLOC(sizeof(t))))
#endif

/* private function prototypes */
static int DRAC3_parse_config_info(struct DRAC3Device * drac3d, const char * info);


/* ------------------------------------------------------------------ */
/* STONITH PLUGIN API                                                 */
/* ------------------------------------------------------------------ */
void *
drac3_new(void)
{
	struct DRAC3Device *drac3d = MALLOCT(struct DRAC3Device);

	if (drac3d == NULL) {
			syslog(LOG_ERR, "out of memory");
			return(NULL);
	}
	memset(drac3d, 0, sizeof(*drac3d));
	drac3d->DRAC3id = DRAC3id;
	drac3d->curl = NULL;
	drac3d->host = NULL;
	drac3d->user = NULL;
	drac3d->pass = NULL;

	return ((void *) drac3d);
}

/* ------------------------------------------------------------------ */
void
drac3_destroy(Stonith * s)
{
	struct DRAC3Device *drac3d;

	if (!ISDRAC3DEV(s)) {
		syslog(LOG_ERR, "%s: invalid argument.", __FUNCTION__);
		return;
	}
	drac3d = (struct DRAC3Device *) s->pinfo;

	drac3d->DRAC3id = NOTdrac3ID;

	/* release curl connection */
	if (drac3d->curl != NULL) {
		drac3Logout(drac3d->curl, drac3d->host);
		curl_easy_cleanup(drac3d->curl);
		drac3d->curl = NULL;
	}

	if (drac3d->host != NULL) {
			FREE(drac3d->host);
			drac3d->host = NULL;
	}
	if (drac3d->user != NULL) {
			FREE(drac3d->user);
			drac3d->user = NULL;
	}
	if (drac3d->pass != NULL) {
			FREE(drac3d->pass);
			drac3d->pass = NULL;
	}

	/* release stonith-object itself */
	FREE(drac3d);
}

/* ------------------------------------------------------------------ */
int 
drac3_set_config_file(Stonith * s, const char *configname) 
{
	FILE *cfgfile;
	char confline[BUFLEN];
	struct DRAC3Device *drac3d;

	if (!ISDRAC3DEV(s)) {
		syslog(LOG_ERR, "%s: invalid argument.", __FUNCTION__);
		return (S_INVAL);
	}

	drac3d = (struct DRAC3Device *) s->pinfo;

	if ((cfgfile = fopen(configname, "r")) == NULL) {
		syslog(LOG_ERR, "Cannot open %s", configname);
		return (S_BADCONFIG);
	}

	while (fgets(confline, sizeof(confline), cfgfile) != NULL) {
		if (*confline == '#' || *confline == '\n' || *confline == EOS)
			continue;
		return (DRAC3_parse_config_info(drac3d, confline));
	}
	return (S_BADCONFIG);
}

/* ------------------------------------------------------------------ */
int
drac3_set_config_info(Stonith * s, const char *info)
{
	struct DRAC3Device *drac3d;

	if (!ISDRAC3DEV(s)) {
		syslog(LOG_ERR, "%s: invalid argument.", __FUNCTION__);
		return (S_OOPS);
	}

	drac3d = (struct DRAC3Device *) s->pinfo;

	return (DRAC3_parse_config_info(drac3d, info));
}

/* ------------------------------------------------------------------ */
const char *
drac3_getinfo(Stonith * s, int reqtype)
{
	struct DRAC3Device *drac3d;
	const char *ret = NULL;

	if (!ISDRAC3DEV(s)) {
		syslog(LOG_ERR, "%s: invalid argument.", __FUNCTION__);
		return (NULL);
	}

	drac3d = (struct DRAC3Device *) s->pinfo;

	switch (reqtype) {
		case ST_DEVICEID:
			ret = drac3d->DRAC3id;
			break;
		case ST_CONF_INFO_SYNTAX:
			ret = _("<drac3-address> <user> <password>\n");
			break;
		case ST_CONF_FILE_SYNTAX:
			ret = _("<drac3-address> <user> <password>\n"
				"All items must be on one line.\n"
				"Blank lines and lines beginning with # are ignored.");
			break;
		case ST_DEVICEDESCR:
			ret = _("Dell DRACIII (via HTTPS)\n"
				"The Dell Remote Access Controller accepts XML commands over HTTPS");
			break;
		case ST_DEVICEURL:
			ret = _("http://www.dell.com/us/en/biz/topics/power_ps2q02-bell.htm");
			break;
		default:
			ret = NULL;
			break;
	}

	return(ret);
}

/* ------------------------------------------------------------------ */
int
drac3_status(Stonith  *s)
{
	struct DRAC3Device *drac3d;

	if (!ISDRAC3DEV(s)) {
		syslog(LOG_ERR, "%s: invalid argument.", __FUNCTION__);
		return (S_INVAL);
	}

	if (!ISCONFIGED(s)) {
		syslog(LOG_ERR, "%s: device is UNCONFIGURED!", __FUNCTION__);
		return (S_OOPS);
	}

	drac3d = (struct DRAC3Device *) s->pinfo;

	if (drac3VerifyLogin(drac3d->curl, drac3d->host)) {
		if (drac3Login(drac3d->curl, drac3d->host,
		                drac3d->user, drac3d->pass)) {
		 	syslog(LOG_ERR, "%s: cannot log into %s at %s", 
							__FUNCTION__,
							DEVICE,
							drac3d->host);
		 	return(S_ACCESS);
		}
	}

	if (drac3GetSysInfo(drac3d->curl, drac3d->host)) 
		return(S_ACCESS);
	else
		return(S_OK);
}

/* ------------------------------------------------------------------ */
int
drac3_reset_req(Stonith * s, int request, const char *host)
{
	struct DRAC3Device *drac3d;
	int rc = S_OK;

	if (!ISDRAC3DEV(s)) {
		syslog(LOG_ERR, "%s: invalid argument.", __FUNCTION__);
		return (S_INVAL);
	}

	if (!ISCONFIGED(s)) {
		syslog(LOG_ERR, "%s: device is UNCONFIGURED!", __FUNCTION__);
		return (S_OOPS);
	}

	drac3d = (struct DRAC3Device *) s->pinfo;

	if (drac3VerifyLogin(drac3d->curl, drac3d->host)) {
		if (drac3Login(drac3d->curl, drac3d->host,
		                drac3d->user, drac3d->pass)) {
		 	syslog(LOG_ERR, "%s: cannot log into %s at %s", 
							__FUNCTION__,
							DEVICE,
							drac3d->host);
		 	return(S_ACCESS);
		}
	}

	switch(request) {
#if defined(ST_POWERON) && defined(ST_POWEROFF)
		case ST_POWERON:
		case ST_POWEROFF:
			// TODO...
#endif
		case ST_GENERIC_RESET:
			if (drac3PowerCycle(drac3d->curl, drac3d->host))
				rc = S_ACCESS;
			break;
		default:
			rc = S_INVAL;
			break;
	}

	return(rc);
}

/* ------------------------------------------------------------------ */
char **
drac3_hostlist(Stonith * s)
{
	struct DRAC3Device *drac3d;
	char **hl;

	if (!ISDRAC3DEV(s)) {
		syslog(LOG_ERR, "%s: invalid argument.", __FUNCTION__);
		return (NULL);
	}

	if (!ISCONFIGED(s)) {
		syslog(LOG_ERR, "%s: device is UNCONFIGURED!", __FUNCTION__);
		return (NULL);
	}

	drac3d = (struct DRAC3Device *) s->pinfo;

	hl = (char **)MALLOC(2*sizeof(char*));
	if (hl == NULL) {
		syslog(LOG_ERR, "%s: out of memory", __FUNCTION__);
	} else {
		hl[1]=NULL;
		hl[0]=STRDUP(drac3d->host);
		if (hl[0]) {
			syslog(LOG_ERR, "%s: out of memory", __FUNCTION__);
			FREE(hl);
			hl = NULL;
		}
		g_strdown(hl[0]);
	}

	return(hl);
}

/* ------------------------------------------------------------------ */
void
drac3_free_hostlist (char ** hlist)
{
	char ** hl = hlist;

        if (hl == NULL) {
                return;
        }
        while (*hl) {
                FREE(*hl);
                *hl = NULL;
                ++hl;
        }
        FREE(hlist);
}

			         
/* ------------------------------------------------------------------ */
/* PRIVATE FUNCTIONS                                                  */
/* ------------------------------------------------------------------ */

static int
DRAC3_parse_config_info(struct DRAC3Device * drac3d, const char * info)
{
	static char host[BUFLEN];
	static char user[BUFLEN];
	static char pass[BUFLEN];
	CURL *curl;

	/* TODO: check strings length in conffile */
	if (sscanf(info, "%s %s %s", host, user, pass) == 3) {

			if ((drac3d->host = STRDUP(host)) == NULL) {
					syslog(LOG_ERR, "%s: out of memory", 
							__FUNCTION__);
					return(S_OOPS);
			}
			g_strdown(drac3d->host);
			if ((drac3d->user = STRDUP(user)) == NULL) {
					syslog(LOG_ERR, "%s: out of memory", 
							__FUNCTION__);
					FREE(drac3d->host);
					return(S_OOPS);
			}
			if ((drac3d->pass = STRDUP(pass)) == NULL) {
					syslog(LOG_ERR, "%s: out of memory", 
							__FUNCTION__);
					FREE(drac3d->host);
					FREE(drac3d->user);
					return(S_OOPS);
			}

			curl = curl_easy_init();
			if ((drac3d->curl = curl_easy_init()) == NULL) { 
					syslog(LOG_ERR, "%s: cannot init curl", 
							__FUNCTION__);
					FREE(drac3d->host);
					FREE(drac3d->user);
					FREE(drac3d->pass);
					return(S_OOPS);
			}

			drac3InitCurl(drac3d->curl);

			return(S_OK);
	} else {
			return(S_BADCONFIG);
	}
}


syntax highlighted by Code2HTML, v. 0.9.1