⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 apcmaster.c

📁 在LINUX下实现HA的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* $Id: apcmaster.c,v 1.10.2.2 2004/12/15 16:43:49 alan Exp $ *//***  Copyright 2001 Mission Critical Linux, Inc.**  All Rights Reserved.*//* *	Stonith module for APC Master Switch (AP9211) * *  Copyright (c) 2001 Mission Critical Linux, Inc. *  author: mike ledoux <mwl@mclinux.com> *  author: Todd Wheeling <wheeling@mclinux.com> * *  Based strongly on original code from baytech.c by Alan Robertson. * * 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 * *//*                          Observations/Notes *  * 1. The APC MasterSwitch, unlike the BayTech network power switch, *    accepts only one (telnet) connection/session at a time. When one *    session is active, any subsequent attempt to connect to the MasterSwitch  *    will result in a connection refused/closed failure. In a cluster  *    environment or other environment utilizing polling/monitoring of the  *    MasterSwitch (from multiple nodes), this can clearly cause problems.  *    Obviously the more nodes and the shorter the polling interval, the more  *    frequently such errors/collisions may occur. * * 2. We observed that on busy networks where there may be high occurances *    of broadcasts, the MasterSwitch became unresponsive.  In some  *    configurations this necessitated placing the power switch onto a  *    private subnet. *//* * Version string that is filled in by CVS */#include <portability.h>static const char *version __attribute__ ((unused)) = "$Revision: 1.10.2.2 $"; #include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <syslog.h>#include <libintl.h>#include <sys/wait.h>#include <glib.h>#include <stonith/stonith.h>#define PIL_PLUGINTYPE          STONITH_TYPE#define PIL_PLUGINTYPE_S        STONITH_TYPE_S#define PIL_PLUGIN              apcmaster#define PIL_PLUGIN_S            "apcmaster"#define PIL_PLUGINLICENSE 	LICENSE_LGPL#define PIL_PLUGINLICENSEURL 	URL_LGPL#include <pils/plugin.h>#include "stonith_signal.h"/* * apcmasterclose is called as part of unloading the apcmaster STONITH plugin. * If there was any global data allocated, or file descriptors opened, etc. * which is associated with the plugin, and not a single interface * in particular, here's our chance to clean it up. */static voidapcmasterclosepi(PILPlugin*pi){}/* * apcmastercloseintf called as part of shutting down the apcmaster STONITH * interface.  If there was any global data allocated, or file descriptors * opened, etc.  which is associated with the apcmaster implementation, * here's our chance to clean it up. */static PIL_rcapcmastercloseintf(PILInterface* pi, void* pd){	return PIL_OK;}static void *		apcmaster_new(void);static void		apcmaster_destroy(Stonith *);static int		apcmaster_set_config_file(Stonith *, const char * cfgname);static int		apcmaster_set_config_info(Stonith *, const char * info);static const char *	apcmaster_getinfo(Stonith * s, int InfoType);static int		apcmaster_status(Stonith * );static int		apcmaster_reset_req(Stonith * s, int request, const char * host);static char **		apcmaster_hostlist(Stonith  *);static void		apcmaster_free_hostlist(char **);static struct stonith_ops apcmasterOps ={	apcmaster_new,		/* Create new STONITH object	*/	apcmaster_destroy,		/* Destroy STONITH object	*/	apcmaster_set_config_file,	/* set configuration from file	*/	apcmaster_set_config_info,	/* Get configuration from file	*/	apcmaster_getinfo,		/* Return STONITH info string	*/	apcmaster_status,		/* Return STONITH device status	*/	apcmaster_reset_req,		/* Request a reset */	apcmaster_hostlist,		/* Return list of supported hosts */	apcmaster_free_hostlist	/* free above list */};PIL_PLUGIN_BOILERPLATE("1.0", Debug, apcmasterclosepi);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->StartProcessPIL_rcPIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);PIL_rcPIL_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	,	&apcmasterOps	,	apcmastercloseintf		/*close */	,	&OurInterface	,	(void*)&OurImports	,	&interfprivate); }#define	DEVICE	"APC MasterSwitch"#define N_(text)	(text)#define _(text)		dgettext(ST_TEXTDOMAIN, text)/* *	I have an AP9211.  This code has been tested with this switch. */struct APCMS {	const char *	MSid;	char *		idinfo;	char *		unitid;	pid_t		pid;	int		rdfd;	int		wrfd;	int		config;	char *		device;        char *		user;	char *		passwd;};static const char * MSid = "APCMS-Stonith";static const char * NOTmsid = "Hey dummy, this has been destroyed (APCMS)";#define	ISAPCMS(i)	(((i)!= NULL && (i)->pinfo != NULL)	\	&& ((struct APCMS *)(i->pinfo))->MSid == MSid)#define	ISCONFIGED(i)	(ISAPCMS(i) && ((struct APCMS *)(i->pinfo))->config)#ifndef MALLOCT#	define     MALLOCT(t)      ((t *)(MALLOC(sizeof(t)))) #endif#define WHITESPACE	" \t\n\r\f"#define	REPLSTR(s,v)	{					\			if ((s) != NULL) {			\				FREE(s);			\				(s)=NULL;			\			}					\			(s) = STRDUP(v);			\			if ((s) == NULL) {			\				syslog(LOG_ERR, _("out of memory"));\			} 					\			}/* *	Different expect strings that we get from the APC MasterSwitch */#define APCMSSTR	"American Power Conversion"static struct Etoken EscapeChar[] =	{ {"Escape character is '^]'.", 0, 0}					,	{NULL,0,0}};static struct Etoken login[] = 		{ {"User Name :", 0, 0}, {NULL,0,0}};static struct Etoken password[] =	{ {"Password  :", 0, 0} ,{NULL,0,0}};static struct Etoken Prompt[] =	{ {"> ", 0, 0} ,{NULL,0,0}};static struct Etoken LoginOK[] =	{ {APCMSSTR, 0, 0}                    , {"User Name :", 1, 0} ,{NULL,0,0}};static struct Etoken Separator[] =	{ {"-----", 0, 0} ,{NULL,0,0}};/* Accept either a CR/NL or an NL/CR */static struct Etoken CRNL[] =		{ {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}};/* We may get a notice about rebooting, or a request for confirmation */static struct Etoken Processing[] =	{ {"Press <ENTER> to continue", 0, 0}				,	{"Enter 'YES' to continue", 1, 0}				,	{NULL,0,0}};static int	MSLookFor(struct APCMS* ms, struct Etoken * tlist, int timeout);static int	MS_connect_device(struct APCMS * ms);static int	MSLogin(struct APCMS * ms);static int	MSRobustLogin(struct APCMS * ms);static int	MSNametoOutlet(struct APCMS*, const char * name);static int	MSReset(struct APCMS*, int outletNum, const char * host);static int	MSScanLine(struct APCMS* ms, int timeout, char * buf, int max);static int	MSLogout(struct APCMS * ms);static void	MSkillcomm(struct APCMS * ms);static int	apcmaster_parse_config_info(struct APCMS* ms, const char * info);#if defined(ST_POWERON) && defined(ST_POWEROFF)static int	apcmaster_onoff(struct APCMS*, int outletnum, const char * unitid,		int request);#endifstatic void	apcmaster_destroy(Stonith *);static void *	apcmaster_new(void);/* *	We do these things a lot.  Here are a few shorthand macros. */#define	SEND(s)         (write(ms->wrfd, (s), strlen(s)))#define	EXPECT(p,t)	{						\			if (MSLookFor(ms, p, t) < 0)			\				return(errno == ETIMEDOUT			\			?	S_TIMEOUT : S_OOPS);			\			}#define	NULLEXPECT(p,t)	{						\				if (MSLookFor(ms, p, t) < 0)		\					return(NULL);			\			}#define	SNARF(s, to)	{						\				if (MSScanLine(ms,to,(s),sizeof(s))	\				!=	S_OK)				\					return(S_OOPS);			\			}#define	NULLSNARF(s, to)	{					\				if (MSScanLine(ms,to,(s),sizeof(s))	\				!=	S_OK)				\					return(NULL);			\				}/* Look for any of the given patterns.  We don't care which */static intMSLookFor(struct APCMS* ms, struct Etoken * tlist, int timeout){	int	rc;	if ((rc = EXPECT_TOK(ms->rdfd, tlist, timeout, NULL, 0)) < 0) {		syslog(LOG_ERR, _("Did not find string: '%s' from" DEVICE ".")		,	tlist[0].string);		MSkillcomm(ms);	}	return(rc);}/* Read and return the rest of the line */static intMSScanLine(struct APCMS* ms, int timeout, char * buf, int max){	if (EXPECT_TOK(ms->rdfd, CRNL, timeout, buf, max) < 0) {		syslog(LOG_ERR, ("Could not read line from " DEVICE "."));		MSkillcomm(ms);		return(S_OOPS);	}	return(S_OK);}/* Login to the APC Master Switch */static intMSLogin(struct APCMS * ms){        /*EXPECT(EscapeChar, 10);*/        if (MSLookFor(ms, EscapeChar, 10) < 0) {		sleep(1);		return (errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);	}  	/* 	 * We should be looking at something like this:         *	User Name :	 */	EXPECT(login, 10);	SEND(ms->user);       	SEND("\r");	/* Expect "Password  :" */	EXPECT(password, 10);	SEND(ms->passwd);	SEND("\r"); 	switch (MSLookFor(ms, LoginOK, 30)) {		case 0:	/* Good! */			syslog(LOG_INFO, _("Successful login to " DEVICE "."));			break;		case 1:	/* Uh-oh - bad password */			syslog(LOG_ERR, _("Invalid password for " DEVICE "."));			return(S_ACCESS);		default:			MSkillcomm(ms);			return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);	} 	return(S_OK);}/* Attempt to login up to 20 times... */static intMSRobustLogin(struct APCMS * ms){	int rc = S_OOPS;	int j = 0;	for ( ; ; ) {	  if (ms->pid > 0)	    MSkillcomm(ms);	  if (MS_connect_device(ms) != S_OK) {		    MSkillcomm(ms);	  }	  else {	    rc = MSLogin(ms);	    if( rc == S_OK ) break;	  }	  if ((++j) == 20) break;	  else sleep(1);	}	return rc;}/* Log out of the APC Master Switch */static int MSLogout(struct APCMS* ms){	int	rc;	/* Make sure we're in the right menu... */ 	/*SEND("\033\033\033\033\033\033\033"); */        SEND("\033");	EXPECT(Prompt, 5);        SEND("\033");	EXPECT(Prompt, 5);        SEND("\033");	EXPECT(Prompt, 5);        SEND("\033");	EXPECT(Prompt, 5);	SEND("\033");		/* Expect "> " */	rc = MSLookFor(ms, Prompt, 5);	/* "4" is logout */	SEND("4\r");	MSkillcomm(ms);	return(rc >= 0 ? S_OK : (errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS));}static voidMSkillcomm(struct APCMS* ms){        if (ms->rdfd >= 0) {		close(ms->rdfd);		ms->rdfd = -1;	}	if (ms->wrfd >= 0) {		close(ms->wrfd);		ms->wrfd = -1;	}	if (ms->pid > 0) {		STONITH_KILL(ms->pid, SIGKILL);		(void)waitpid(ms->pid, NULL, 0);		ms->pid = -1;	}}/* Reset (power-cycle) the given outlets */static intMSReset(struct APCMS* ms, int outletNum, const char *host){  	char		unum[32];	/* Make sure we're in the top level menu */        SEND("\033");	EXPECT(Prompt, 5);        SEND("\033");	EXPECT(Prompt, 5);        SEND("\033");	EXPECT(Prompt, 5);        SEND("\033");	EXPECT(Prompt, 5);	SEND("\033");		/* Expect ">" */	EXPECT(Prompt, 5);	/* Request menu 1 (Device Control) */	SEND("1\r");	/* Select requested outlet */	EXPECT(Prompt, 5);	snprintf(unum, sizeof(unum), "%i\r", outletNum);  	SEND(unum);	/* Select menu 1 (Control Outlet) */	EXPECT(Prompt, 5);	SEND("1\r");	/* Select menu 3 (Immediate Reboot) */	EXPECT(Prompt, 5);	SEND("3\r");	/* Expect "Press <ENTER> " or "Enter 'YES'" (if confirmation turned on) */	retry:	switch (MSLookFor(ms, Processing, 5)) {		case 0: /* Got "Press <ENTER>" Do so */			SEND("\r");			break;		case 1: /* Got that annoying command confirmation :-( */			SEND("YES\r");			goto retry;		default: 			return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);	}	syslog(LOG_INFO, _("Host %s being rebooted."), host);	/* Expect ">" */	if (MSLookFor(ms, Prompt, 10) < 0) {		return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);	}	/* All Right!  Power is back on.  Life is Good! */	syslog(LOG_INFO, _("Power restored to host %s."), host);	/* Return to top level menu */	SEND("\033");	EXPECT(Prompt, 5);        SEND("\033");	EXPECT(Prompt, 5);        SEND("\033");	EXPECT(Prompt, 5);        SEND("\033");	EXPECT(Prompt, 5);	SEND("\033");	EXPECT(Prompt, 5);	SEND("\033");	return(S_OK);}#if defined(ST_POWERON) && defined(ST_POWEROFF)static intapcmaster_onoff(struct APCMS* ms, int outletNum, const char * unitid, int req){	char		unum[32];	const char *	onoff = (req == ST_POWERON ? "1\r" : "2\r");	int	rc;	if ((rc = MSRobustLogin(ms) != S_OK)) {		syslog(LOG_ERR, _("Cannot log into " DEVICE "."));		return(rc);	}		/* Make sure we're in the top level menu */        SEND("\033");	EXPECT(Prompt, 5);        SEND("\033");	EXPECT(Prompt, 5);        SEND("\033");	EXPECT(Prompt, 5);        SEND("\033");	EXPECT(Prompt, 5);	SEND("\033");	/* Expect ">" */	EXPECT(Prompt, 5);	/* Request menu 1 (Device Control) */	SEND("1\r");	/* Select requested outlet */  	snprintf(unum, sizeof(unum), "%d\r", outletNum);   	SEND(unum); 	/* Select menu 1 (Control Outlet) */	SEND("1\r");	/* Send ON/OFF command for given outlet */	SEND(onoff);	/* Expect "Press <ENTER> " or "Enter 'YES'" (if confirmation turned on) */	retry:	switch (MSLookFor(ms, Processing, 5)) {		case 0: /* Got "Press <ENTER>" Do so */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -