📄 apcsmart.c
字号:
/* $Id: apcsmart.c,v 1.12.2.4 2005/04/22 13:53:26 blaschke Exp $ *//* * Stonith module for APCSmart Stonith device * Copyright (c) 2000 Andreas Piesk <a.piesk@gmx.net> * 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 * * UPS code is taken from: * 'Network UPS Tools' by Russell Kroll <rkroll@exploits.org> * homepage: http://www.exploits.org/nut/ */#include <portability.h>#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <syslog.h>#include <libintl.h>#include <fcntl.h>#include <termios.h>#include <sys/wait.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/file.h>#include <stonith/stonith.h>#include <glib.h>/* * APCSmart (tested with old 900XLI, APC SmartUPS 700 and SmartUPS-1000) * * The reset is a combined reset: "S" and "@000" * The "S" command tells the ups that if it is on-battery, it should * remain offline until the power is back. * If that command is not accepted, the "@000" command will be sent * to tell the ups to turn off and back on right away. * In both cases, if the UPS supports a 20 second shutdown grace * period (such as on the 900XLI), the shutdown will delay that long, * otherwise the shutdown will happen immediately (the code searches * for the smallest possible delay). */#define DEVICE "APCSmart-Stonith"#define CFG_FILE "/etc/ha.d/apcsmart.cfg"#define MAX_DEVICES 1#define SERIAL_TIMEOUT 3 /* timeout in sec */#define SEND_DELAY 50000 /* in 祍 */#define ENDCHAR 10 /* use LF */#define MAX_STRING 512#define MAX_DELAY_STRING 16#define SWITCH_TO_NEXT_VAL "-" /* APC cmd for cycling through * the values */#define CMD_SMART_MODE "Y"#define RSP_SMART_MODE "SM"#define CMD_GET_STATUS "Q"#define RSP_GET_STATUS NULL#define CMD_RESET "S" /* turn off & stay off if on battery */#define CMD_RESET2 "@000" /* turn off & immediately turn on */#define RSP_RESET "*" /* RESET response from older models */#define RSP_RESET2 "OK" /* RESET response from newer models */#define CMD_SHUTDOWN_DELAY "p"#define CMD_WAKEUP_DELAY "r"#define CR 13struct APCDevice { const char *APCid; /* of object */ char **hostlist; /* served by the device (only 1) */ int hostcount; /* of hosts (1) */ char *upsdev; /* */ int upsfd; /* for serial port */ char shutdown_delay[MAX_DELAY_STRING]; /* smallest shutdown delay */ char old_shutdown_delay[MAX_DELAY_STRING]; /* old shutdown delay */ char wakeup_delay[MAX_DELAY_STRING]; /* smallest wakeup delay */ char old_wakeup_delay[MAX_DELAY_STRING]; /* old wakeup delay */};/* saving old settings */static struct termios old_tio;static int f_serialtimeout; /* flag for timeout */static const char *APCid = DEVICE;static const char *NOTapcID = "destroyed (APCSmart)";#define ISAPCDEV(i) (((i)!= NULL && (i)->pinfo != NULL) && \ ((struct APCDevice *)(i->pinfo))->APCid == APCid)#define ISCONFIGED(i) (((struct APCDevice *)(i->pinfo))->upsdev != NULL)#define _(text) dgettext(ST_TEXTDOMAIN, text)/* * stonith prototypes */#define PIL_PLUGINTYPE STONITH_TYPE#define PIL_PLUGINTYPE_S STONITH_TYPE_S#define PIL_PLUGIN apcsmart#define PIL_PLUGIN_S "apcsmart"#define PIL_PLUGINLICENSE LICENSE_LGPL#define PIL_PLUGINLICENSEURL URL_LGPL#include <pils/plugin.h>#include "stonith_signal.h"/* * apcsmartclose is called as part of unloading the apcsmart 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 voidapcsmartclosepi(PILPlugin*pi){}/* * apcsmartcloseintf called as part of shutting down the apcsmart STONITH * interface. If there was any global data allocated, or file descriptors * opened, etc. which is associated with the apcsmart implementation, * here's our chance to clean it up. */static PIL_rcapcsmartcloseintf(PILInterface* pi, void* pd){ return PIL_OK;}static void * apcsmart_new(void);static void apcsmart_destroy(Stonith *);static int apcsmart_set_config_file(Stonith *, const char * cfgname);static int apcsmart_set_config_info(Stonith *, const char * info);static const char * apcsmart_getinfo(Stonith * s, int InfoType);static int apcsmart_status(Stonith * );static int apcsmart_reset_req(Stonith * s, int request, const char * host);static char ** apcsmart_hostlist(Stonith *);static void apcsmart_free_hostlist(char **);static struct stonith_ops apcsmartOps ={ apcsmart_new, /* Create new STONITH object */ apcsmart_destroy, /* Destroy STONITH object */ apcsmart_set_config_file, /* set configuration from file */ apcsmart_set_config_info, /* Get configuration from file */ apcsmart_getinfo, /* Return STONITH info string */ apcsmart_status, /* Return STONITH device status */ apcsmart_reset_req, /* Request a reset */ apcsmart_hostlist, /* Return list of supported hosts */ apcsmart_free_hostlist /* free above list */};PIL_PLUGIN_BOILERPLATE("1.0", Debug, apcsmartclosepi);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#undef MALLOCT#define MALLOCT(t) ((t *)(MALLOC(sizeof(t))))PIL_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 , &apcsmartOps , apcsmartcloseintf /*close */ , &OurInterface , (void*)&OurImports , &interfprivate); }/* * own prototypes */int APC_open_serialport(const char *port, speed_t speed);void APC_close_serialport(int upsfd);void APC_sh_serial_timeout(int sig);int APC_send_cmd(int upsfd, const char *cmd);int APC_recv_rsp(int upsfd, char *rsp);int APC_enter_smartmode(int upsfd);int APC_set_ups_var(int upsfd, const char *cmd, char *newval);int APC_get_smallest_delay(int upsfd, const char *cmd, char *smdelay);int APC_parse_config_info(struct APCDevice *ad, const char *info );int APC_init( struct APCDevice *ad );void APC_deinit( struct APCDevice *ad );/* * * Portable locking (non-blocking) * * This is a candidate for including in a general portability library. */static intfile_lock(int fd){ int ret;#ifdef HAVE_FCNTL struct flock l; l.l_type = F_WRLCK; l.l_whence = 0; l.l_start = 0; l.l_len = 0; ret = fcntl(fd, F_SETLK, &l); return((ret == -1) ? -1 : 0);#else# ifdef HAVE_FLOCK ret = flock(fd, LOCK_EX | LOCK_NB); return(ret);# else# error "No locking method (flock, fcntl) is available" return(-1);# endif /* HAVE_FLOCK */#endif /* HAVE_FCNTL */}static intfile_unlock(int fd){ int ret;#ifdef HAVE_FCNTL struct flock l; l.l_type = F_UNLCK; l.l_whence = 0; l.l_start = 0; l.l_len = 0; ret = fcntl(fd, F_SETLK, &l); return((ret == -1) ? -1 : 0);#else# ifdef HAVE_FLOCK ret = flock(fd, LOCK_UN); return(ret);# else# error "No unlocking method (flock, fcntl) is available" return(-1);# endif /* HAVE_FLOCK */#endif /* HAVE_FCNTL */}/* * Signal handler for serial port timeouts */voidAPC_sh_serial_timeout(int sig){#ifdef APC_DEBUG syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);#endif STONITH_IGNORE_SIG(SIGALRM);#ifdef APC_DEBUG syslog(LOG_DEBUG, "%s: serial port timed out.", __FUNCTION__);#endif f_serialtimeout = TRUE; return;}/* * Open serial port and set it to b2400 */intAPC_open_serialport(const char *port, speed_t speed){ struct termios tio; int fd;#ifdef APC_DEBUG syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);#endif STONITH_SIGNAL(SIGALRM, APC_sh_serial_timeout); alarm(SERIAL_TIMEOUT); f_serialtimeout = FALSE; fd = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK | O_EXCL); alarm(0); STONITH_IGNORE_SIG(SIGALRM); if (fd < 0) {#ifdef APC_DEBUG syslog(LOG_DEBUG, "%s: 1st open failed.", __FUNCTION__);#endif return -1; } if (file_lock(fd) != 0) {#ifdef APC_DEBUG syslog(LOG_DEBUG, "%s: 1st lock failed.", __FUNCTION__);#endif close(fd); return -1; } tcgetattr(fd, &old_tio); memcpy(&tio, &old_tio, sizeof(struct termios)); tio.c_lflag = 0 | ECHOE | ECHOKE | ECHOCTL | PENDIN; tio.c_iflag = 0 | IXANY | IMAXBEL | IXOFF; tio.c_oflag = 0 | ONLCR; tio.c_cflag = 0 | CREAD | CS8 | HUPCL | CLOCAL; cfsetispeed(&tio, speed); cfsetospeed(&tio, speed); tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; tcflush(fd, TCIFLUSH); tcsetattr(fd, TCSANOW, &tio); close(fd); STONITH_SIGNAL(SIGALRM, APC_sh_serial_timeout); alarm(SERIAL_TIMEOUT); fd = open(port, O_RDWR | O_NOCTTY | O_EXCL); alarm(0); STONITH_IGNORE_SIG(SIGALRM); if (fd < 0) {#ifdef APC_DEBUG syslog(LOG_DEBUG, "%s: 2nd open failed.", __FUNCTION__);#endif return -1; } if (file_lock(fd) != 0) {#ifdef APC_DEBUG syslog(LOG_DEBUG, "%s: 2nd lock failed.", __FUNCTION__);#endif close(fd); return -1; } tcgetattr(fd, &tio); tio.c_cflag = CS8 | CLOCAL | CREAD; tio.c_iflag = IGNPAR; tio.c_oflag = 0; tio.c_lflag = 0; tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; cfsetispeed(&tio, speed); cfsetospeed(&tio, speed); tcflush(fd, TCIFLUSH); tcsetattr(fd, TCSANOW, &tio); return (fd);}/* * Close serial port and restore old settings */voidAPC_close_serialport(int upsfd){#ifdef APC_DEBUG syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);#endif file_unlock(upsfd); tcflush(upsfd, TCIFLUSH); tcsetattr(upsfd, TCSANOW, &old_tio); close(upsfd);}/* * Send a command to the ups */intAPC_send_cmd(int upsfd, const char *cmd){ int i;#ifdef APC_DEBUG syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);#endif for (i = strlen(cmd); i > 0; i--) { tcflush(upsfd, TCIFLUSH); if (write(upsfd, cmd++, 1) != 1) return (S_ACCESS); usleep(SEND_DELAY); } return (S_OK);}/* * Get the response from the ups */intAPC_recv_rsp(int upsfd, char *rsp){ char *p = rsp; char inp; int num = 0;#ifdef APC_DEBUG syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);#endif *p = '\0'; STONITH_SIGNAL(SIGALRM, APC_sh_serial_timeout); alarm(SERIAL_TIMEOUT); while (num < MAX_STRING) { if (read(upsfd, &inp, 1) == 1) { /* shutdown sends only a '*' without LF */ if ((inp == '*') && (num == 0)) { *p++ = inp; num++; inp = ENDCHAR; } if (inp == ENDCHAR) { alarm(0); STONITH_IGNORE_SIG(SIGALRM); *p = '\0'; return (S_OK); } if (inp != CR) { *p++ = inp; num++; } } else { alarm(0); STONITH_IGNORE_SIG(SIGALRM); *p = '\0'; return (f_serialtimeout ? S_TIMEOUT : S_ACCESS); } } return (S_ACCESS);}/* * Enter smart mode */intAPC_enter_smartmode(int upsfd){ int rc; char resp[MAX_STRING];#ifdef APC_DEBUG syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);#endif strcpy( resp, RSP_SMART_MODE); if (((rc = APC_send_cmd(upsfd, CMD_SMART_MODE)) == S_OK) && ((rc = APC_recv_rsp(upsfd, resp)) == S_OK) && (strcmp(RSP_SMART_MODE, resp) == 0)) return (S_OK); return (S_ACCESS);}/* * Set a value in the hardware using the <cmdchar> '-' (repeat) approach */intAPC_set_ups_var(int upsfd, const char *cmd, char *newval){ char resp[MAX_STRING]; char orig[MAX_STRING]; int rc;#ifdef APC_DEBUG syslog(LOG_DEBUG, "%s: called.", __FUNCTION__);#endif if (((rc = APC_enter_smartmode(upsfd)) != S_OK) || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK) || ((rc = APC_recv_rsp(upsfd, orig)) != S_OK)) return (rc);#ifdef APC_DEBUG syslog(LOG_DEBUG, "%s: var '%s' original val %s", __FUNCTION__, cmd, orig);#endif if (strcmp(orig, newval) == 0) return (S_OK); /* already set */ *resp = '\0'; while (strcmp(resp, orig) != 0) { if (((rc = APC_send_cmd(upsfd, SWITCH_TO_NEXT_VAL)) != S_OK) || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) return (rc); if (((rc = APC_enter_smartmode(upsfd)) != S_OK) || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK) || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) return (rc); if (strcmp(resp, newval) == 0) {#ifdef APC_DEBUG syslog(LOG_DEBUG, "%s: var '%s' set to %s", __FUNCTION__, cmd, newval);#endif strcpy(newval, orig); /* return the old value */ return (S_OK); /* got it */ } } syslog(LOG_ERR, "%s(): variable '%s' wrapped!", __FUNCTION__, cmd); syslog(LOG_ERR, "%s(): This UPS may not support STONITH :-(" , __FUNCTION__); return (S_OOPS);}/* * Query the smallest delay supported by the hardware using the * <cmdchar> '-' (repeat) approach and looping through all possible values, * saving the smallest */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -