diff --git a/fence/agents/ipmilan/ipmilan.c b/fence/agents/ipmilan/ipmilan.c index 3561456d..83029e32 100644 --- a/fence/agents/ipmilan/ipmilan.c +++ b/fence/agents/ipmilan/ipmilan.c @@ -1,1278 +1,1279 @@ /** @file * clumanager 1.2.x linux-cluster fence and/or GFS fence * module for Intel/Bull/Dell Tiger4 machines via IPMI over lan. * (Probably works with anything ipmitool can control, though.) * * Note: REQUIRES ipmitool to operate. On certain machines, the hardware * manufacturer provides this tool for you. Otherwise, check: * * http://ipmitool.sourceforge.net * */ #include "clusterautoconfig.h" #include #include #include #include #include #include #include #include #include #include /* fenced doesn't use the remote calls */ #define ST_STATUS 0 #define ST_POWERON 1 #define ST_POWEROFF 2 #define ST_GENERIC_RESET 3 #define ST_CYCLE 4 #define ST_DIAG 5 #define DEFAULT_TIMEOUT 20 #define DEFAULT_POWER_WAIT 2 #define DEFAULT_METHOD "onoff" /* We should follow FenceAgentsAPI standard*/ #define ERR_OFF_SUCCESSFUL 0 #define ERR_OFF_FAIL 1 #define ERR_ON_SUCCESSFUL 0 #define ERR_ON_FAIL 1 #define ERR_STATUS_ON 0 #define ERR_STATUS_FAIL 1 #define ERR_STATUS_OFF 2 #define ERR_OK 0 #define log(lvl, fmt, args...) fprintf(stderr, fmt, ##args) #include #include "copyright.cf" #include "expect.h" #define IPMIID "IPMI over LAN driver" #define NOTIPMI "Destroyed IPMI over LAN driver" #define dbg_printf(i, lvl, fmt, args...) \ do { \ if ( (i)->i_verbose >= lvl) { \ printf(fmt, ##args); \ fflush(stdout); \ } \ } while (0) struct ipmi { const char *i_id; const char *i_ipmitool; char *i_host; char *i_user; char *i_authtype; char *i_password; char *i_privlvl; int i_rdfd; int i_wrfd; pid_t i_pid; int i_config; int i_verbose; int i_lanplus; int i_timeout; int i_power_wait; int i_cipher; }; /* Supported installation paths */ const char *ipmitool_paths[] = { "/usr/local/bull/NSMasterHW/bin/ipmitool", "/usr/bin/ipmitool", "/usr/sbin/ipmitool", "/bin/ipmitool", "/sbin/ipmitool", "/usr/local/bin/ipmitool", "/usr/local/sbin/ipmitool", NULL }; #define ECIPHER 2048 #define ESTATE (8192*2) static struct Etoken power_on_complete[] = { {"Password:", EPERM, 0}, {"Unable to establish LAN", EAGAIN, 0}, /* Retry */ {"IPMI mutex", EFAULT, 0}, /* Death */ {"Unsupported cipher suite ID", ECIPHER,0}, {"read_rakp2_message: no support for", ECIPHER,0}, {"Up/On", 0, 0}, {NULL, 0, 0} }; static struct Etoken power_off_complete[] = { {"Password:", EPERM, 0}, {"Unable to establish LAN", EAGAIN, 0}, /* Retry */ {"IPMI mutex", EFAULT, 0}, /* Death */ {"Unsupported cipher suite ID", ECIPHER,0}, {"read_rakp2_message: no support for", ECIPHER,0}, {"Down/Off", 0, 0}, {NULL, 0, 0} }; /** Powercycle operation */ static struct Etoken power_cycle_complete[] = { {"Password:", EPERM, 0}, {"Unable to establish LAN", EAGAIN, 0}, /* Retry */ {"IPMI mutex", EFAULT, 0}, /* Death */ {"Unsupported cipher suite ID", ECIPHER,0}, {"read_rakp2_message: no support for", ECIPHER,0}, {"Command not supported in present state", ESTATE, 0}, {": Cycle", 0, 0}, {NULL, 0, 0} }; #define STATE_OFF 4096 #define STATE_ON 8192 static struct Etoken power_status[] = { {"Password:", EPERM, 0}, {"Unable to establish LAN", EAGAIN, 0}, /* Retry */ {"IPMI mutex", EFAULT, 0}, /* Death */ {"Unsupported cipher suite ID", ECIPHER,0}, {"read_rakp2_message: no support for", ECIPHER,0}, {"Chassis Power is off", STATE_OFF, 0}, {"Chassis Power is on", STATE_ON, 0}, {NULL, 0, 0} }; /* Structure describing one xml metadata value*/ struct xml_parameter_s { const char *name; const char *getopt; const int required; const char *content_type; const char *default_value; const char *description; }; /* Array of xml metadatas*/ struct xml_parameter_s xml_parameters[]={ {"auth","-A",0,"string",NULL,"IPMI Lan Auth type (md5, password, or none)"}, {"ipaddr","-a",1,"string",NULL,"IPMI Lan IP to talk to"}, {"passwd","-p",0,"string",NULL,"Password (if required) to control power on IPMI device"}, {"passwd_script","-S",0,"string",NULL,"Script to retrieve password (if required)"}, {"lanplus","-P",0,"boolean",NULL,"Use Lanplus to improve security of connection"}, {"login","-l",0,"string",NULL,"Username/Login (if required) to control power on IPMI device"}, {"action","-o",0,"string","reboot","Operation to perform. Valid operations: on, off, reboot, status, list, diag, monitor or metadata"}, {"timeout","-t",0,"string",NULL,"Timeout (sec) for IPMI operation"}, {"cipher","-C",0,"string",NULL,"Ciphersuite to use (same as ipmitool -C parameter)"}, {"method","-M",0,"string",DEFAULT_METHOD,"Method to fence (onoff or cycle)"}, {"power_wait","-T",0,"string","2","Wait X seconds after on/off operation"}, {"delay","-f",0,"string",NULL,"Wait X seconds before fencing is started"}, {"privlvl","-L",0,"string",NULL,"Privilege level on IPMI device"}, {"verbose","-v",0,"boolean",NULL,"Verbose mode"}}; /* Search for ipmitool */ static const char * ipmitool_path(void) { char *p; int x = 0; struct stat sb; for (x = 0; ipmitool_paths[x]; x++) { p = (char *)ipmitool_paths[x]; if (stat(p, &sb) != 0) continue; if (!S_ISREG(sb.st_mode)) continue; /* executable? */ if ((sb.st_mode & S_IXUSR) == 0) continue; return (const char *)p; } return NULL; } /** Prepare string for use in sh style environment. This function take source string and prepend/append quote (') to start/end of source string to dest string. Any occurence of quote in source string is replaced by '\'' sequence. Dest string must be preallocated. @param dest Destination string @param source Source string @param max_len Maximum length of data written to dest string (including end 0) @return Pointer to start of destination string. */ static char *str_prepare_for_sh(char *dest,char *source,int max_len) { char *dest_p=dest; char *max_dest=dest+max_len; if (dest_p+1>=max_dest) {*dest_p=0;return dest;} *dest_p++='\''; while (*source) { if (*source=='\'') { if (dest_p+4>=max_dest) {*dest_p=0;return dest;} memcpy(dest_p,"'\\''",4);dest_p+=4; } else { if (dest_p+1>=max_dest) {*dest_p=0;return dest;} *dest_p++=*source; } source++; } if (dest_p+2>=max_dest) {*dest_p=0;return dest;} *dest_p++='\'';*dest_p=0; return dest; } static int build_cmd(char *command, size_t cmdlen, struct ipmi *ipmi, int op, int nopass) { char cmd[2048]; char arg[2048]; char tmp[2048]; int x; /* Store path */ if (ipmi->i_lanplus) { snprintf(cmd, sizeof(cmd), "%s -I lanplus -H %s", ipmi->i_ipmitool, str_prepare_for_sh(tmp,ipmi->i_host,sizeof(tmp))); } else { snprintf(cmd, sizeof(cmd), "%s -I lan -H %s", ipmi->i_ipmitool, str_prepare_for_sh(tmp,ipmi->i_host,sizeof(tmp))); } if (ipmi->i_user) { snprintf(arg, sizeof(arg), " -U %s", str_prepare_for_sh(tmp,ipmi->i_user,sizeof(tmp))); strncat(cmd, arg, sizeof(cmd) - strlen(arg)); } if (ipmi->i_authtype) { snprintf(arg, sizeof(arg), " -A %s", str_prepare_for_sh(tmp,ipmi->i_authtype,sizeof(tmp))); strncat(cmd, arg, sizeof(cmd) - strlen(arg)); } if (ipmi->i_privlvl) { snprintf(arg, sizeof(arg), " -L %s", str_prepare_for_sh(tmp,ipmi->i_privlvl,sizeof(tmp))); strncat(cmd, arg, sizeof(cmd) - strlen(arg)); } if (ipmi->i_cipher>=0) { snprintf(arg, sizeof(arg), " -C %d", ipmi->i_cipher); strncat(cmd, arg, sizeof(cmd) - strlen(arg)); } if (ipmi->i_password) { if (nopass) { snprintf(arg, sizeof(arg), " -P %s", str_prepare_for_sh(tmp,(char *)"[set]",sizeof(tmp))); } else { snprintf(arg, sizeof(arg), " -P %s", str_prepare_for_sh(tmp,ipmi->i_password,sizeof(tmp))); } strncat(cmd, arg, sizeof(cmd) - strlen(arg)); } else { snprintf(arg, sizeof(arg), " -P ''"); strncat(cmd, arg, sizeof(cmd) - strlen(arg)); } /* Tack on the -v flags for ipmitool; in most cases, i_verbose will be 0 */ for (x = 0; x < ipmi->i_verbose; x++) { snprintf(arg, sizeof(arg), " -v"); strncat(cmd, arg, sizeof(cmd) - strlen(arg)); } switch(op) { case ST_POWERON: snprintf(arg, sizeof(arg), "%s chassis power on", cmd); break; case ST_POWEROFF: snprintf(arg, sizeof(arg), "%s chassis power off", cmd); break; case ST_STATUS: snprintf(arg, sizeof(arg), "%s chassis power status", cmd); break; case ST_CYCLE: snprintf(arg, sizeof(arg), "%s chassis power cycle", cmd); break; case ST_DIAG: snprintf(arg, sizeof(arg), "%s chassis power diag", cmd); break; } strncpy(command, arg, cmdlen); return 0; } static int ipmi_spawn(struct ipmi *ipmi, const char *cmd, const char *cmd_print) { if (!ipmi) { errno = EINVAL; return -1; } dbg_printf(ipmi, 1, "Spawning: '%s'...\n", cmd_print); if (ipmi->i_pid != -1) { dbg_printf(ipmi, 1, "Can't spawn: PID %d running\n", (int)ipmi->i_pid); errno = EINPROGRESS; return -1; } if ((ipmi->i_pid = StartProcess(cmd, &ipmi->i_rdfd, &ipmi->i_wrfd, EXP_STDERR|EXP_NOCTTY)) >= 0) { dbg_printf(ipmi, 2, "Spawned: '%s' - PID %d\n", cmd_print, (int)ipmi->i_pid); return 0; } return -1; } static int ipmi_reap(struct ipmi *ipmi) { if (ipmi->i_pid >= 0) { dbg_printf(ipmi, 2, "Reaping pid %d\n", ipmi->i_pid); kill(ipmi->i_pid, 9); waitpid(ipmi->i_pid, NULL, 0); } ipmi->i_pid = -1; if (ipmi->i_rdfd >= 0) { close(ipmi->i_rdfd); ipmi->i_rdfd = -1; } if (ipmi->i_wrfd >= 0) { close(ipmi->i_wrfd); ipmi->i_wrfd = -1; } return 0; } static int ipmi_expect(struct ipmi *ipmi, struct Etoken *toklist, int timeout) { int ret; char buf[32768]; /* XX hope this is enough */ dbg_printf(ipmi, 3, "Looking for: \n"); for (ret = 0; toklist[ret].string; ret++) { dbg_printf(ipmi, 3, " '%s', val = %d\n", toklist[ret].string, toklist[ret].toktype); } ret = ExpectToken(ipmi->i_rdfd, toklist, timeout, buf, sizeof(buf)); dbg_printf(ipmi, 3, "ExpectToken returned %d\n", ret); if (ret == -1) { ret = errno; dbg_printf(ipmi, 3, "ExpectToken failed. Info returned:\n"); dbg_printf(ipmi, 3, ">>>>>\n%s\n<<<<<\nError = %d (%s)\n", buf, ret, strerror(ret)); } return ret; } static int ipmi_op(struct ipmi *ipmi, int op, struct Etoken *toklist) { char cmd[2048]; char cmd_nopass[2048]; char *cmd_print = cmd; int ret; build_cmd(cmd, sizeof(cmd), ipmi, op, 0); if (ipmi->i_verbose >= 1 && ipmi->i_password) { build_cmd(cmd_nopass, sizeof(cmd_nopass), ipmi, op, 1); cmd_print = cmd_nopass; } if (ipmi_spawn(ipmi, cmd, cmd_print) != 0) return -1; ret = ipmi_expect(ipmi, toklist, ipmi->i_timeout); ipmi_reap(ipmi); if (ret == EFAULT) { log(LOG_CRIT, "ipmilan: ipmitool failed to create " "mutex; unable to complete operation\n"); return ret; } if (ret == ECIPHER) { log(LOG_CRIT, "ipmilan: ipmitool failed to operate " "with ciphersuite %d; unable to complete operation\n",ipmi->i_cipher); return ret; } if (ret == ESTATE) { log(LOG_CRIT, "ipmilan: ipmitool failed to complete " "command in current state\n"); return ret; } if (ret == ETIMEDOUT) { /*!!! Still couldn't get through?! */ log(LOG_WARNING, "ipmilan: Failed to connect after %d seconds\n",ipmi->i_timeout); } return ret; } static int ipmi_off(struct ipmi *ipmi) { int ret, retries = 7; ret = ipmi_op(ipmi, ST_STATUS, power_status); switch(ret) { case STATE_ON: break; case STATE_OFF: return 0; default: return ret; } while (retries>=0) { ret = ipmi_op(ipmi, ST_POWEROFF, power_off_complete); if (ret != 0) return ret; sleep(ipmi->i_power_wait); --retries; ret = ipmi_op(ipmi, ST_STATUS, power_status); switch(ret) { case STATE_OFF: return 0; case EFAULT: /* We're done. */ retries = 0; break; case STATE_ON: default: continue; } } log(LOG_WARNING, "ipmilan: Power still on\n"); return ret; } static int ipmi_on(struct ipmi *ipmi) { int ret, retries = 7; ret = ipmi_op(ipmi, ST_STATUS, power_status); switch(ret) { case STATE_ON: return 0; case STATE_OFF: break; default: return ret; } while (retries>=0) { ret = ipmi_op(ipmi, ST_POWERON, power_on_complete); if (ret != 0) return ret; sleep(ipmi->i_power_wait); --retries; ret = ipmi_op(ipmi, ST_STATUS, power_status); switch(ret) { case STATE_ON: return 0; case EFAULT: /* We're done. */ retries = 0; break; case STATE_OFF: default: continue; } } log(LOG_WARNING, "ipmilan: Power still off\n"); return ret; } static int ipmi_cycle(struct ipmi *ipmi) { int ret; ret = ipmi_op(ipmi, ST_CYCLE, power_cycle_complete); return ret; } static int ipmi_diag(struct ipmi *ipmi) { int ret; ret = ipmi_op(ipmi, ST_DIAG, power_off_complete); return ret; } /** Squash all our private data */ static void ipmi_destroy(struct ipmi *i) { ipmi_reap(i); if (i->i_user) { free(i->i_user); i->i_user = NULL; } if (i->i_password) { free(i->i_password); i->i_password= NULL; } if (i->i_host) { free(i->i_host); i->i_host = NULL; } if (i->i_privlvl) { free(i->i_privlvl); i->i_privlvl = NULL; } i->i_config = 0; i->i_id = NOTIPMI; } /** Multipurpose initializer. Used to either create a new, blank ipmi, or update an existing one, or both. */ static struct ipmi * ipmi_init(struct ipmi *i, char *host, char *authtype, char *user, char *password, int lanplus, int verbose,int timeout, int power_wait, int cipher, char *privlvl) { const char *p; if (!i || !i->i_ipmitool) p = ipmitool_path(); else p = i->i_ipmitool; if (!p) { log(LOG_WARNING, "ipmilan: ipmitool not found!\n"); return NULL; } if (!i) i = malloc (sizeof(*i)); if (!i) return NULL; if (host && strlen(host)) { i->i_host = strdup(host); if (!i->i_host) { free(i); return NULL; } } else i->i_host = NULL; if (password && strlen(password)) { i->i_password = strdup(password); if (!i->i_password) { free(i->i_host); free(i); return NULL; } } else i->i_password = NULL; if (authtype && strlen(authtype)) { i->i_authtype = strdup(authtype); if (!i->i_authtype) { free(i->i_host); if (i->i_password) free(i->i_password); free(i); return NULL; } } else i->i_authtype = NULL; if (privlvl && strlen(privlvl)) { i->i_privlvl = strdup(privlvl); } else i->i_privlvl = NULL; if (user && strlen(user)) { i->i_user= strdup(user); if (!i->i_user) { free(i->i_host); if (i->i_authtype) free(i->i_authtype); if (i->i_password) free(i->i_password); free(i); return NULL; } } else i->i_user = NULL; i->i_ipmitool = p; i->i_rdfd = -1; i->i_wrfd = -1; i->i_pid = -1; i->i_id = IPMIID; i->i_verbose = verbose; i->i_lanplus = lanplus; i->i_timeout = timeout; i->i_power_wait = power_wait; i->i_cipher = cipher; return i; } /** Remove leading and trailing whitespace from a line of text. */ static int cleanup(char *line, size_t linelen) { char *p; size_t x; /* Remove leading whitespace. */ p = line; for (x = 0; x <= linelen; x++) { switch (line[x]) { case '\t': case ' ': break; case '\n': case '\r': return -1; default: goto eol; } } eol: /* Move the remainder down by as many whitespace chars as we chewed up */ if (x) memmove(p, &line[x], linelen-x); /* Remove trailing whitespace. */ for (x=0; x <= linelen; x++) { switch(line[x]) { case '\t': case '\r': case '\n': line[x] = 0; case 0: /* End of line */ return 0; } } return -1; } /** Parse args from stdin. Dev + devlen + op + oplen must be valid. */ static int get_options_stdin(char *ip, size_t iplen, char *authtype, size_t atlen, char *passwd, size_t pwlen, char *pwd_script, size_t pwd_script_len, char *user, size_t userlen, char *op, size_t oplen, int *lanplus, int *verbose,int *timeout, int *power_wait, int *cipher, char *method, int methodlen, char *delay, size_t delaylen, char *privlvl, size_t privlen) { char in[256]; int line = 0; char *name, *val; op[0] = 0; method[0] = 0; while (fgets(in, sizeof(in), stdin)) { ++line; if (in[0] == '#') continue; if (cleanup(in, sizeof(in)) == -1) continue; name = in; if ((val = strchr(in, '='))) { *val = 0; ++val; } if (!strcasecmp(name, "agent")) { /* Used by fenced? */ } else if (!strcasecmp(name, "verbose")) { *verbose = 1; } else if (!strcasecmp(name, "ipaddr")) { /* IP address to use. E.g. 10.1.1.2 */ if (val) strncpy(ip, val, iplen); else ip[0] = 0; } else if (!strcasecmp(name, "auth")) { /* Authtype to use */ if (val) strncpy(authtype, val, atlen); else authtype[0] = 0; } else if (!strcasecmp(name, "passwd")) { /* password */ if (val) strncpy(passwd, val, pwlen); else passwd[0] = 0; } else if (!strcasecmp(name, "passwd_script")) { if (val) { strncpy(pwd_script, val, pwd_script_len); pwd_script[pwd_script_len - 1] = '\0'; } else pwd_script[0] = '\0'; } else if (!strcasecmp(name, "privlvl")) { if (val) { strncpy(privlvl, val, privlen); } else privlvl[0] = '\0'; } else if (!strcasecmp(name, "user") || !strcasecmp(name, "login")) { /* username */ if (val) strncpy(user, val, userlen); else user[0] = 0; } else if (!strcasecmp(name, "lanplus")) { (*lanplus) = 1; } else if (!strcasecmp(name,"timeout")) { if ((!val) || (sscanf(val,"%d",timeout)!=1) || *timeout<1) { *timeout=DEFAULT_TIMEOUT; } } else if (!strcasecmp(name,"power_wait")) { if ((!val) || (sscanf(val,"%d",power_wait)!=1) || *power_wait<1) { *power_wait=DEFAULT_POWER_WAIT; } } else if (!strcasecmp(name,"cipher")) { if ((!val) || (sscanf(val,"%d",cipher)!=1) || *cipher<0) { *cipher=-1; } } else if (!strcasecmp(name,"method")) { if (val) strncpy (method, val, methodlen); else method[0] = 0; } else if (!strcasecmp(name, "option") || !strcasecmp(name, "operation") || !strcasecmp(name, "action")) { if (val) strncpy(op, val, oplen); else op[0] = 0; } else if (!strcasecmp(name,"delay")) { if (val) strncpy(delay, val, delaylen); else delay[0] = 0; } } return 0; } /** Print a message to stderr and call exit(1). */ static void fail_exit(const char *msg) { fprintf(stderr, "failed: %s\n", msg); exit(1); } static void usage_exit(char *pname) { printf("usage: %s \n", pname); printf(" -A IPMI Lan Auth type (md5, password, or none)\n"); printf(" -a IPMI Lan IP to talk to\n"); printf(" -i IPMI Lan IP to talk to (deprecated, use -a)\n"); printf(" -p Password (if required) to control power on\n" " IPMI device\n"); printf(" -P Use Lanplus to improve security of connection\n"); printf(" -S Script to retrieve password (if required)\n"); printf(" -l Username/Login (if required) to control power\n" " on IPMI device\n"); printf(" -L IPMI privilege level. Defaults to ADMINISTRATOR.\n" " See ipmitool(1) for more info.\n"); printf(" -o Operation to perform.\n"); printf(" Valid operations: on, off, reboot, status,\n"); printf(" diag, list or monitor\n"); printf(" -t Timeout (sec) for IPMI operation (default %d)\n",DEFAULT_TIMEOUT); printf(" -T Wait X seconds after on/off operation\n"); printf(" -f Wait X seconds before fencing is started\n"); printf(" -C Ciphersuite to use (same as ipmitool -C parameter)\n"); printf(" -M Method to fence (onoff or cycle (default %s)\n", DEFAULT_METHOD); printf(" -V Print version and exit\n"); printf(" -v Verbose mode\n\n"); printf("If no options are specified, the following options will be read\n"); printf("from standard input (one per line):\n\n"); printf(" auth= Same as -A\n"); printf(" ipaddr=<#> Same as -a\n"); printf(" passwd= Same as -p\n"); printf(" passwd_script= Same as -S\n"); printf(" lanplus Same as -P\n"); printf(" login= Same as -l\n"); printf(" option= Same as -o\n"); printf(" operation= Same as -o\n"); printf(" action= Same as -o\n"); printf(" delay= Same as -f\n"); printf(" timeout= Same as -t\n"); printf(" power_wait=