Fix various bugs; see ChangeLog
[clumanager.git] / src / stonithlib / ipmilan.c
blob7a41e0a0048517e1d5118b6ac6650f288af0d2de
1 /** @file
2 * clumanager 1.2.x STONITH and/or linux-cluster fence and/or GFS fence
3 * module for Intel/Bull/Dell Tiger4 machines via IPMI over lan.
4 * (Probably works with anything ipmitool can control, though.)
6 * Note: REQUIRES ipmitool to operate. On certain machines, the hardware
7 * manufacturer provides this tool for you. Otherwise, check:
9 * http://ipmitool.sourceforge.net
11 * Copyright 2005 Red Hat, Inc.
12 * author: Lon Hohberger <lhh at redhat.com>
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <signal.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <sys/wait.h>
36 #include <sys/stat.h>
37 #include <libintl.h>
39 #ifndef FENCE
40 #include <syslog.h>
41 #include "stonith.h"
42 #define log(lvl, fmt, args...) \
43 do { \
44 syslog(lvl, fmt, ##args); \
45 fprintf(stderr, "%s: " fmt, #lvl, ##args); \
46 } while(0)
47 #else
48 /* fenced doesn't use the remote calls */
49 #define ST_STATUS 0
50 #define ST_POWERON 1
51 #define ST_POWEROFF 2
52 #define ST_GENERIC_RESET 3
54 #define log(lvl, fmt, args...) fprintf(stderr, fmt, ##args)
55 #include <libgen.h>
57 #ifndef TESTING
58 #include <copyright.cf>
59 #else
60 #define REDHAT_COPYRIGHT "Copyright (C) 2005 Red Hat, Inc.\n"
61 #define FENCE_RELEASE_NAME "TEST ONLY; Not for distribution\n"
63 #endif
64 #endif
66 #include "expect.h"
68 #define IPMIID 0x6301fee
69 #define NOTIPMI 0x00010010
71 struct ipmi {
72 const char *i_ipmitool;
73 char *i_host;
74 char *i_user;
75 char *i_password;
76 int i_rdfd;
77 int i_wrfd;
78 pid_t i_pid;
79 int i_config;
80 int i_id;
85 Supported installation paths
87 const char *ipmitool_paths[] = {
88 "/usr/local/bull/NSMasterHW/bin/ipmitool",
89 "/usr/bin/ipmitool",
90 "/usr/sbin/ipmitool",
91 "/bin/ipmitool",
92 "/sbin/ipmitool",
93 "/usr/local/bin/ipmitool",
94 "/usr/local/sbin/ipmitool",
95 NULL
99 static struct Etoken power_on_complete[] = {
100 {"Password:", EPERM, 0},
101 {"Unable to establish LAN", EAGAIN, 0}, /* Retry */
102 {"IPMI mutex", EFAULT, 0}, /* Death */
103 {"Up/On", 0, 0},
104 {NULL, 0, 0}
107 static struct Etoken power_off_complete[] = {
108 {"Password:", EPERM, 0},
109 {"Unable to establish LAN", EAGAIN, 0}, /* Retry */
110 {"IPMI mutex", EFAULT, 0}, /* Death */
111 {"Down/Off", 0, 0},
112 {NULL, 0, 0}
116 #define STATE_OFF 4096
117 #define STATE_ON 8192
118 static struct Etoken power_status[] = {
119 {"Password:", EPERM, 0},
120 {"Unable to establish LAN", EAGAIN, 0}, /* Retry */
121 {"IPMI mutex", EFAULT, 0}, /* Death */
122 {"Chassis Power is off", STATE_OFF, 0},
123 {"Chassis Power is on", STATE_ON, 0},
124 {NULL, 0, 0}
129 Search for ipmitool
131 static const char *
132 ipmitool_path(void)
134 char *p;
135 int x = 0;
136 struct stat sb;
138 for (x = 0; ipmitool_paths[x]; x++) {
139 p = (char *)ipmitool_paths[x];
140 if (stat(p, &sb) != 0)
141 continue;
143 if (!S_ISREG(sb.st_mode))
144 continue;
146 /* executable? */
147 if ((sb.st_mode & S_IXUSR) == 0)
148 continue;
150 return (const char *)p;
153 return NULL;
157 static int
158 build_cmd(char *command, size_t cmdlen, struct ipmi *ipmi, int op)
160 char cmd[2048];
161 char arg[2048];
163 /* Store path */
164 snprintf(cmd, sizeof(cmd), "%s -I lan -H %s", ipmi->i_ipmitool,
165 ipmi->i_host);
167 if (ipmi->i_user) {
168 snprintf(arg, sizeof(arg), " -U %s", ipmi->i_user);
169 strncat(cmd, arg, sizeof(cmd) - strlen(arg));
172 if (ipmi->i_password) {
173 snprintf(arg, sizeof(arg), " -P %s", ipmi->i_password);
174 strncat(cmd, arg, sizeof(cmd) - strlen(arg));
177 switch(op) {
178 case ST_POWERON:
179 snprintf(arg, sizeof(arg),
180 "%s chassis power on", cmd);
181 break;
182 case ST_POWEROFF:
183 snprintf(arg, sizeof(arg),
184 "%s chassis power off", cmd);
185 break;
186 case ST_STATUS:
187 snprintf(arg, sizeof(arg),
188 "%s chassis power status", cmd);
189 break;
192 strncpy(command, arg, cmdlen);
193 return 0;
197 static int
198 ipmi_spawn(struct ipmi *ipmi, const char *cmd)
200 if (!ipmi) {
201 errno = EINVAL;
202 return -1;
205 if (ipmi->i_pid != -1) {
206 errno = EINPROGRESS;
207 return -1;
210 if ((ipmi->i_pid = StartProcess(cmd, &ipmi->i_rdfd,
211 &ipmi->i_wrfd,
212 EXP_STDERR|EXP_NOCTTY)) >= 0)
213 return 0;
214 return -1;
218 static int
219 ipmi_reap(struct ipmi *ipmi)
221 if (ipmi->i_pid >= 0) {
222 kill(ipmi->i_pid, 9);
223 waitpid(ipmi->i_pid, NULL, 0);
225 ipmi->i_pid = -1;
226 if (ipmi->i_rdfd >= 0) {
227 close(ipmi->i_rdfd);
228 ipmi->i_rdfd = -1;
230 if (ipmi->i_wrfd >= 0) {
231 close(ipmi->i_wrfd);
232 ipmi->i_wrfd = -1;
234 return 0;
238 static int
239 ipmi_expect(struct ipmi *ipmi, struct Etoken *toklist, int timeout)
241 int ret;
243 ret = ExpectToken(ipmi->i_rdfd, toklist, timeout, NULL, 0);
244 if (ret == -1)
245 ret = errno;
247 return ret;
251 static int
252 ipmi_op(struct ipmi *ipmi, int op, struct Etoken *toklist)
254 char cmd[2048];
255 int retries = 5;
256 int ret;
258 build_cmd(cmd, sizeof(cmd), ipmi, op);
260 if (ipmi_spawn(ipmi, cmd) != 0)
261 return -1;
262 ret = ipmi_expect(ipmi, toklist, 120);
263 ipmi_reap(ipmi);
265 while ((ret == EAGAIN || ret == ETIMEDOUT) && retries > 0) {
266 sleep(5);
267 --retries;
269 if (ipmi_spawn(ipmi, cmd) != 0)
270 return -1;
271 ret = ipmi_expect(ipmi, toklist, 120);
272 if (ret == EFAULT) {
273 /* Doomed. */
274 break;
276 ipmi_reap(ipmi);
279 if (ret == EFAULT) {
280 log(LOG_CRIT, "ipmilan: ipmitool failed to create "
281 "mutex; unable to complete operation\n");
282 return ret;
285 if (ret == EAGAIN) {
286 /*!!! Still couldn't get through?! */
287 log(LOG_WARNING,
288 "ipmilan: Failed to connect after 30 seconds\n");
291 return ret;
295 static int
296 ipmi_off(struct ipmi *ipmi)
298 int ret, retries = 5;
300 ret = ipmi_op(ipmi, ST_STATUS, power_status);
301 switch(ret) {
302 case STATE_ON:
303 break;
304 case STATE_OFF:
305 return 0;
306 default:
307 return ret;
310 ret = ipmi_op(ipmi, ST_POWEROFF, power_off_complete);
311 if (ret != 0)
312 return ret;
314 while (retries>=0) {
315 sleep(5);
316 --retries;
317 ret = ipmi_op(ipmi, ST_STATUS, power_status);
319 switch(ret) {
320 case STATE_OFF:
321 return 0;
322 case EFAULT:
323 /* We're done. */
324 retries = 0;
325 break;
326 case STATE_ON:
327 default:
328 continue;
331 log(LOG_WARNING, "ipmilan: Power still on\n");
333 return ret;
337 static int
338 ipmi_on(struct ipmi *ipmi)
340 int ret, retries = 5;
342 ret = ipmi_op(ipmi, ST_STATUS, power_status);
343 switch(ret) {
344 case STATE_ON:
345 return 0;
346 case STATE_OFF:
347 break;
348 default:
349 return ret;
352 ret = ipmi_op(ipmi, ST_POWERON, power_on_complete);
353 if (ret != 0)
354 return ret;
356 while (retries>=0) {
357 sleep(5);
358 --retries;
359 ret = ipmi_op(ipmi, ST_STATUS, power_status);
361 switch(ret) {
362 case STATE_ON:
363 return 0;
364 case EFAULT:
365 /* We're done. */
366 retries = 0;
367 break;
368 case STATE_OFF:
369 default:
370 continue;
373 log(LOG_WARNING, "ipmilan: Power still off\n");
375 return ret;
380 Squash all our private data
382 static void
383 ipmi_destroy(struct ipmi *i)
385 ipmi_reap(i);
386 if (i->i_user) {
387 free(i->i_user);
388 i->i_user = NULL;
390 if (i->i_password) {
391 free(i->i_password);
392 i->i_password= NULL;
394 if (i->i_host) {
395 free(i->i_host);
396 i->i_host = NULL;
398 i->i_config = 0;
399 i->i_id = NOTIPMI;
404 Multipurpose initializer. Used to either create a new, blank ipmi,
405 or update an existing one, or both.
407 static struct ipmi *
408 ipmi_init(struct ipmi *i, char *host, char *user, char *password)
410 const char *p;
412 if (!i || !i->i_ipmitool)
413 p = ipmitool_path();
414 else
415 p = i->i_ipmitool;
417 if (!p) {
418 log(LOG_WARNING, "ipmilan: ipmitool not found!\n");
419 return NULL;
422 if (!i)
423 i = malloc (sizeof(*i));
424 if (!i)
425 return NULL;
427 if (host && strlen(host)) {
428 i->i_host = strdup(host);
429 if (!i->i_host) {
430 free(i);
431 return NULL;
433 } else
434 i->i_host = NULL;
436 if (password && strlen(password)) {
437 i->i_password = strdup(password);
438 if (!i->i_password) {
439 free(i->i_host);
440 free(i);
441 return NULL;
443 } else
444 i->i_password = NULL;
446 if (user && strlen(user)) {
447 i->i_user= strdup(user);
448 if (!i->i_user) {
449 free(i->i_host);
450 if (i->i_password)
451 free(i->i_password);
452 free(i);
453 return NULL;
455 } else
456 i->i_user = NULL;
457 i->i_ipmitool = p;
458 i->i_rdfd = -1;
459 i->i_wrfd = -1;
460 i->i_pid = -1;
461 i->i_id = IPMIID;
463 return i;
467 #ifndef FENCE
469 STONITH operations
471 #define ISIPMI(s) (s && s->pinfo && ((struct ipmi *)s->pinfo)->i_id == IPMIID)
473 const char *
474 st_getinfo(Stonith * __attribute__ ((unused)) s, int __attribute__((unused))i)
476 return "Not really useful info";
479 void
480 st_destroy(Stonith *s)
482 struct ipmi *i;
484 if (!ISIPMI(s)) {
485 log(LOG_ERR, "st_destroy(IPMI): Invalid Argument");
486 return;
489 i = (struct ipmi *)s->pinfo;
490 ipmi_destroy(i);
494 void *
495 st_new(void)
497 struct ipmi *i;
499 i = malloc(sizeof(*i));
500 if (!i) {
501 log(LOG_ERR, "st_new(IPMI) %s", strerror(errno));
502 return NULL;
505 memset((void *)i, 0, sizeof(*i));
506 ipmi_init(i, NULL, NULL, NULL);
507 return i;
512 st_status(Stonith *s)
514 int ret;
516 if (!ISIPMI(s))
517 return S_OOPS;
519 ret = ipmi_op((struct ipmi *)s->pinfo, ST_STATUS, power_status);
520 if (ret == STATE_ON || ret == STATE_OFF)
521 return S_OK;
523 /* Permission denied? */
524 if (ret == EPERM)
525 return S_BADCONFIG;
527 return S_OOPS;
532 st_reset(Stonith *s, int req, char * port)
534 int ret;
536 switch(req) {
537 case ST_POWERON:
538 ret = ipmi_on((struct ipmi *)s->pinfo);
539 break;
540 case ST_POWEROFF:
541 ret = ipmi_off((struct ipmi *)s->pinfo);
542 break;
543 case ST_GENERIC_RESET:
544 /* Could use chassis power cycle, but this works too*/
545 ret = ipmi_off((struct ipmi *)s->pinfo);
546 if (ret == 0)
547 ipmi_on((struct ipmi *)s->pinfo);
548 break;
549 default:
550 return S_OOPS;
553 switch(ret) {
554 case 0:
555 /* Success */
556 return S_OK;
557 case EFAULT:
558 log(LOG_CRIT, "ipmilan: unable to complete request\n");
559 return S_OOPS;
560 case EPERM:
561 return S_BADCONFIG;
562 case ETIMEDOUT:
563 return S_BADCONFIG;
564 case STATE_ON:
565 log(LOG_ERR, "Power to host %s still ON\n", port);
566 break;
567 case STATE_OFF:
568 log(LOG_WARNING, "Power to host %s still OFF\n", port);
569 break;
572 return S_RESETFAIL;
577 Complicated parser. Has to deal with whitespace as well as the
578 possibility of having 1, 2, or 3 arguments.
580 static int
581 _ipmilan_setconfinfo(Stonith *s, const char *info)
583 char info_priv[1024];
584 char *host = info_priv;
585 char *user = NULL;
586 char *passwd = NULL;
587 char *end = NULL;
588 struct ipmi *i;
589 size_t len;
591 if (!ISIPMI(s))
592 return S_OOPS;
593 i = (struct ipmi *)s->pinfo;
595 snprintf(info_priv, sizeof(info_priv), "%s", info);
596 len = strcspn(host, "\n\r\t ");
597 if (len >= strlen(host))
598 user = NULL;
599 else
600 user = host + len;
601 if (user) {
602 *user = 0;
603 user++;
606 /* No separator or end of string reached */
607 if (user && *user) {
609 len = strcspn(user, "\n\r\t ");
610 if (len >= strlen(user))
611 passwd = NULL;
612 else
613 passwd = user + len;
614 if (passwd) {
615 *passwd = 0;
616 passwd++;
619 /* We don't need a username for this one */
620 if (!passwd || !*passwd) {
621 passwd = user;
622 user = NULL;
625 len = strcspn(passwd, "\n\r\t ");
626 end = passwd + len;
627 if (*end)
628 *end = 0;
631 if (!*user || !strcmp(user, "(null)"))
632 user = NULL;
634 i = ipmi_init(i, host, user, passwd);
635 if (!i)
636 return S_OOPS;
637 i->i_config = 1;
639 return S_OK;
644 st_setconfinfo(Stonith *s, const char *info)
646 /* XXX dlmap collisions? */
647 return _ipmilan_setconfinfo(s, info);
651 /* -- Ripped from old STONITH drivers.
652 * Parse the information in the given configuration file,
653 * and stash it away...
656 st_setconffile(Stonith* s, const char * configname)
658 FILE * cfgfile;
659 char sid[256];
661 if (!ISIPMI(s))
662 return S_OOPS;
664 if ((cfgfile = fopen(configname, "r")) == NULL) {
665 printf("Can't open %s\n", configname);
666 log(LOG_ERR, "Cannot open %s", configname);
667 return(S_BADCONFIG);
669 while (fgets(sid, sizeof(sid), cfgfile) != NULL){
670 if (*sid == '#' || *sid == '\n' || *sid == EOS) {
671 continue;
673 fclose(cfgfile);
674 return _ipmilan_setconfinfo(s, sid);
676 fclose(cfgfile);
677 return(S_BADCONFIG);
680 #else
682 /* Fence module instead of STONITH module */
684 Remove leading and trailing whitespace from a line of text.
687 cleanup(char *line, size_t linelen)
689 char *p;
690 int x;
692 /* Remove leading whitespace. */
693 p = line;
694 for (x = 0; x <= linelen; x++) {
695 switch (line[x]) {
696 case '\t':
697 case ' ':
698 break;
699 case '\n':
700 case '\r':
701 return -1;
702 default:
703 goto eol;
706 eol:
707 /* Move the remainder down by as many whitespace chars as we
708 chewed up */
709 if (x)
710 memmove(p, &line[x], linelen-x);
712 /* Remove trailing whitespace. */
713 for (x=0; x <= linelen; x++) {
714 switch(line[x]) {
715 case '\t':
716 case ' ':
717 case '\r':
718 case '\n':
719 line[x] = 0;
720 case 0:
721 /* End of line */
722 return 0;
726 return -1;
731 Parse args from stdin. Dev + devlen + op + oplen must be valid.
734 get_options_stdin(char *ip, size_t iplen,
735 char *passwd, size_t pwlen,
736 char *user, size_t userlen,
737 char *op, size_t oplen,
738 int *verbose)
740 char in[256];
741 int line = 0;
742 char *name, *val;
744 op[0] = 0;
746 while (fgets(in, sizeof(in), stdin)) {
747 ++line;
749 if (in[0] == '#')
750 continue;
752 if (cleanup(in, sizeof(in)) == -1)
753 continue;
755 name = in;
756 if ((val = strchr(in, '='))) {
757 *val = 0;
758 ++val;
761 if (!strcasecmp(name, "agent")) {
762 /* Used by fenced? */
763 } else if (!strcasecmp(name, "verbose")) {
764 *verbose = 1;
765 } else if (!strcasecmp(name, "ipaddr")) {
766 /* IP address to use. E.g. 10.1.1.2 */
767 if (val)
768 strncpy(ip, val, iplen);
769 else
770 ip[0] = 0;
772 } else if (!strcasecmp(name, "passwd")) {
773 /* password */
774 if (val)
775 strncpy(passwd, val, pwlen);
776 else
777 passwd[0] = 0;
779 } else if (!strcasecmp(name, "user")) {
780 /* username */
781 if (val)
782 strncpy(user, val, userlen);
783 else
784 user[0] = 0;
786 } else if (!strcasecmp(name, "option") ||
787 !strcasecmp(name, "operation") ||
788 !strcasecmp(name, "action")) {
789 if (val)
790 strncpy(op, val, oplen);
791 else
792 op[0] = 0;
793 } else {
794 fprintf(stderr,
795 "parse error: illegal name on line %d\n",
796 line);
797 return 1;
801 return 0;
806 Print a message to stderr and call exit(1).
808 void
809 fail_exit(char *msg)
811 fprintf(stderr, "failed: %s\n", msg);
812 exit(1);
815 void
816 usage_exit(char *pname)
818 printf("usage: %s <options>\n", pname);
819 printf(" -i <ipaddr> IPMI Lan IP to talk to\n");
820 printf(" -p <password> Password (if required) to control power on\n"
821 " IPMI device\n");
822 printf(" -l <login> Username/Login (if required) to control power\n"
823 " on IPMI device\n");
824 printf(" -o <op> Operation to perform.\n");
825 printf(" Valid operations: on, off, reboot\n");
826 printf(" -V Print version and exit\n");
827 printf(" -v Verbose mode\n\n");
828 printf("If no options are specified, the following options will be read\n");
829 printf("from standard input (one per line):\n\n");
830 printf(" ipaddr=<#> Same as -i\n");
831 printf(" passwd=<pass> Same as -p\n");
832 printf(" login=<login> Same as -u\n");
833 printf(" option=<op> Same as -o\n");
834 printf(" operation=<op> Same as -o\n");
835 printf(" action=<op> Same as -o\n");
836 printf(" verbose Same as -v\n\n");
837 exit(1);
842 main(int argc, char **argv)
844 extern char *optarg;
845 int opt, ret = -1;
846 char ip[64];
847 char passwd[64];
848 char user[64];
849 char op[64];
850 int verbose=0;
851 char *pname = basename(argv[0]);
852 struct ipmi *i;
854 memset(ip, 0, sizeof(ip));
855 memset(passwd, 0, sizeof(passwd));
856 memset(user, 0, sizeof(user));
858 if (argc > 1) {
860 Parse command line options if any were specified
862 while ((opt = getopt(argc, argv, "i:l:p:o:vV?hH")) != EOF) {
863 switch(opt) {
864 case 'i':
865 /* IP address */
866 strncpy(ip, optarg, sizeof(ip));
867 break;
868 case 'l':
869 /* user / login */
870 strncpy(user, optarg, sizeof(user));
871 break;
872 case 'p':
873 /* password */
874 strncpy(passwd, optarg, sizeof(passwd));
875 break;
876 case 'o':
877 /* Operation */
878 strncpy(op, optarg, sizeof(op));
879 break;
880 case 'v':
881 verbose++;
882 break;
883 case 'V':
884 printf("%s %s (built %s %s)\n", pname,
885 FENCE_RELEASE_NAME,
886 __DATE__, __TIME__);
887 printf("%s\n",
888 REDHAT_COPYRIGHT);
889 return 0;
890 default:
891 usage_exit(pname);
894 } else {
896 No command line args? Get stuff from stdin
898 if (get_options_stdin(ip, sizeof(ip),
899 user, sizeof(user),
900 passwd, sizeof(passwd),
901 op, sizeof(op), &verbose) != 0)
902 return 1;
906 Validate the operating parameters
908 if (strlen(ip) == 0)
909 fail_exit("no IP address specified");
911 if (strcasecmp(op, "off") && strcasecmp(op, "on") &&
912 strcasecmp(op, "reboot")) {
913 fail_exit("operation must be 'on', 'off', or 'reboot'");
916 /* Ok, set up the IPMI struct */
917 i = ipmi_init(NULL, ip, user, passwd);
918 if (!i)
919 fail_exit("Failed to initialize\n");
922 Perform the requested operation
924 if (!strcasecmp(op, "reboot")) {
925 printf("Rebooting machine @ IPMI:%s...", ip);
926 fflush(stdout);
927 ret = ipmi_off(i);
928 if (ret != 0)
929 goto out;
930 ret = ipmi_on(i);
932 } else if (!strcasecmp(op, "on")) {
933 printf("Powering on machine @ IPMI:%s...", ip);
934 fflush(stdout);
935 ret = ipmi_on(i);
937 } else if (!strcasecmp(op, "off")) {
938 printf("Powering off machine @ IPMI:%s...", ip);
939 fflush(stdout);
940 ret = ipmi_off(i);
943 out:
944 ipmi_destroy(i);
945 free(i);
946 if (ret == 0)
947 printf("Done\n");
948 else
949 printf("Failed\n");
950 return ret;
952 #endif /* fence */