mail[conman-users] Conman-libipmiconsole integration


Others Months | Index by Date | Thread Index
>>   [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Header


Content

Posted by Levi Pearson on August 03, 2007 - 02:50:
Here's my work so far on my integration of conman with libipmiconsole
from FreeIPMI.  I've tested it so far only with about 15 nodes, but it
seems to perform well in that case.  I'll be able to test it to a
somewhat larger scale in a week or two.

It could also use some additional configurability, but I think it
integrates fairly well with the conman codebase.  I'd now like to get
some outside eyes on it and let me know where I've made mistakes and
need improvement. :)

I tried to follow the coding conventions I found throughout the rest of
the project, and I made all the code I added conditionally compiled
based on a --with-freeipmi flag passed to configure.

Anyway, let me know what you think.

                --Levi


diff --git a/ChangeLog b/ChangeLog
index 2748c1e..6b0792b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2007-07-30  Levi Pearson  <lpearson@xxxxxxxx>
+
+       * server.c, server.h, server-sock.c, server-obj.c, server-logfile.c,
+         server-esc.c, server-conf.c, server-ipmi.c, configure.ac,
+         config.h.in, Makefile.in:
+         Added an object type to access Serial-over-LAN consoles via IPMI
+         through FreeIPMI's libipmiconsole
+
+       * man/conman.conf.5.in: Updated documentation to reflect SoL consoles
+
 2007-05-24  Chris Dunlap  <cdunlap@xxxxxxxx>
 
        * : Released 0.2.1.
diff --git a/Makefile.in b/Makefile.in
index 9235620..a168ce0 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -14,6 +14,8 @@ CFLAGS=               @CFLAGS@
 CPPFLAGS=      @CPPFLAGS@
 LDFLAGS=       @LDFLAGS@
 LIBS=          @LIBS@
+SLIBS=         @SLIBS@
+SOBJS=         @SOBJS@
 RANLIB=                @RANLIB@
 INSTALL=       @INSTALL@
 @SET_MAKE@
@@ -41,11 +43,10 @@ CLIENT_OBJS=        $(CLIENT_OBJS1) $(COMMON_OBJS)
 SERVER_OBJS1=  server.o server-conf.o server-esc.o server-logfile.o
 SERVER_OBJS2=  server-obj.o server-process.o server-serial.o server-sock.o
 SERVER_OBJS3=  server-telnet.c tpoll.o
-SERVER_OBJS=   $(SERVER_OBJS1) $(SERVER_OBJS2) $(SERVER_OBJS3) $(COMMON_OBJS)
+SERVER_OBJS=   $(SERVER_OBJS1) $(SERVER_OBJS2) $(SERVER_OBJS3) 
$(COMMON_OBJS) $(SOBJS)
 COMMON_LIBS=   -lpthread $(LIBS)
 CLIENT_LIBS=   $(COMMON_LIBS)
-SERVER_LIBS=   $(COMMON_LIBS)
-
+SERVER_LIBS=   $(COMMON_LIBS) $(SLIBS)
 
 all: $(PROGS) tags
 
diff --git a/config.h.in b/config.h.in
index 8e13e54..8f091c2 100644
--- a/config.h.in
+++ b/config.h.in
@@ -114,6 +114,9 @@
 /* Define if using the debug malloc library. */
 #undef WITH_DMALLOC
 
+/* Define if using FreeIPMI's libipmiconsole. */
+#undef WITH_FREEIPMI
+
 /* Define if using Pthreads. */
 #undef WITH_PTHREADS
 
diff --git a/configure.ac b/configure.ac
index 3d15ac3..2b4decf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -236,6 +236,35 @@ fi
 AC_MSG_CHECKING(whether to use TCP Wrappers)
 AC_MSG_RESULT(${tcp_wrappers=no})
 
+dnl Check for FreeIPMI libraries
+dnl
+AC_ARG_WITH(freeipmi,
+  AC_HELP_STRING([--with-freeipmi], [use FreeIPMI's libipmiconsole]),
+  [ case "$withval" in
+      yes) freeipmi=yes ;;
+      no)  freeipmi=no ;;
+      *)   AC_MSG_RESULT(doh!)
+           AC_MSG_ERROR([bad value "$withval" for --with-freeipmi]) ;;
+    esac
+  ]
+)
+if test "$freeipmi" != no; then
+  AC_CHECK_HEADER(ipmiconsole.h, ac_have_ipmiconsole_h=yes)
+  AC_CHECK_LIB(ipmiconsole, ipmiconsole_engine_init, ac_have_ipmiconsole=yes)
+  if test "$ac_have_ipmiconsole_h" = yes -a "$ac_have_ipmiconsole" = yes; 
then
+    AC_DEFINE_UNQUOTED(WITH_FREEIPMI, 1, [Define if using FreeIPMI's 
libipmiconsole.])
+    freeipmi=yes
+    SOBJS="server-ipmi.o $SOJBS"
+    SLIBS="-lipmiconsole $SLIBS"
+  else
+    freeipmi=no
+  fi
+fi
+AC_MSG_CHECKING(whether to use FreeIPMI's libipmiconsole)
+AC_MSG_RESULT(${freeipmi=no})
+AC_SUBST(SOBJS)
+AC_SUBST(SLIBS)
+
 
 dnl Check for ConMan daemon conf file.
 dnl Force a double shell-expansion of the CONF var.
diff --git a/man/conman.conf.5.in b/man/conman.conf.5.in
index 6b4ef09..dce2e55 100644
--- a/man/conman.conf.5.in
+++ b/man/conman.conf.5.in
@@ -136,6 +136,27 @@ for none, '\fBo\fR' for odd, and '\fBe\fR' for even.
 .br
 .sp
 The default is "9600,8n1" for 9600 bps, 8 data bits, no parity, and 1 stop 
bit.
+.TP
+\fBseropts\fR \fB=\fR "\fIusername\fR,\fIpassword,\fR[,\fIK_g key\fR]"
+Specifies global options for IPMI Serial-over-LAN devices.  These options
+can be overriden on a per-console basis by specifying the \fBCONSOLE\fR
+\fBipmiopts\fR keyword.  This directive is only available if compiled with 
the
+\fB--with-freeipmi\fR option.
+.br
+.sp
+\fIusername\fR is a string of no more than 16 characters that specifies the
+username with which to authenticate to the BMCs serving the remote consoles.
+.br
+.sp
+\fIpassword\fR is a string of no more than 20 characters that specifies the
+password with which to authenticate to the BMCs serving the remote consoles.
+.br
+.sp
+\fIK_g key\f\R is a string that specifies the K_g key with which to
+authenticate to the BMCs serving the remote consoles.  The key must be at
+most 20 bytes long, and can be specified directly as a string or in hex,
+in which case this string should begin with "0x" followed by at most 40 hex
+digits.
 
 .SH CONSOLE DIRECTIVES
 This directive defines an individual console being managed by the daemon.
@@ -170,6 +191,9 @@ This keyword is optional (cf., \fBGLOBAL DIRECTIVES\fR).
 .TP
 \fBseropts\fR \fB=\fR "\fIstring\fR"
 This keyword is optional (cf., \fBGLOBAL DIRECTIVES\fR).
+.TP
+\fBipmiopts\fR \fB=\fR "\fIstring\fR"
+This keyword is optional (cf., \fBGLOBAL DIRECTIVES\fR).
 
 .SH CONVERSION SPECIFICATIONS
 A conversion specifier is a two-character sequence beginning with
diff --git a/server-conf.c b/server-conf.c
index a0a5a0c..89aa37b 100644
--- a/server-conf.c
+++ b/server-conf.c
@@ -60,6 +60,9 @@ enum server_conf_toks {
     SERVER_CONF_DEV,
     SERVER_CONF_EXECPATH,
     SERVER_CONF_GLOBAL,
+#ifdef WITH_FREEIPMI
+    SERVER_CONF_IPMIOPTS,
+#endif /* WITH_FREEIPMI */
     SERVER_CONF_KEEPALIVE,
     SERVER_CONF_LOG,
     SERVER_CONF_LOGDIR,
@@ -88,6 +91,9 @@ static char *server_conf_strs[] = {
     "DEV",
     "EXECPATH",
     "GLOBAL",
+#ifdef WITH_FREEIPMI
+    "IPMIOPTS",
+#endif /* WITH_FREEIPMI */
     "KEEPALIVE",
     "LOG",
     "LOGDIR",
@@ -164,6 +170,9 @@ typedef struct console_strs {
     char *log;
     char *lopts;
     char *sopts;
+#ifdef WITH_FREEIPMI
+    char *iopts;
+#endif /* WITH_FREEIPMI */
 } console_strs_t;
 
 
@@ -176,6 +185,9 @@ static int is_telnet_dev(const char *dev, char 
**host_ref, int *port_ref);
 static int is_serial_dev(const char *dev, const char *cwd, char **path_ref);
 static int is_process_dev(const char *dev, const char *cwd,
     const char *exec_path, char **path_ref);
+#ifdef WITH_FREEIPMI
+static int is_ipmi_dev(const char *dev, char **host_ref);
+#endif /* WITH_FREEIPMI */
 static int search_exec_path(const char *path, const char *src,
     char *dst, int dstlen);
 static void parse_global_directive(server_conf_t *conf, Lex l);
@@ -244,6 +256,11 @@ server_conf_t * create_server_conf(void)
     conf->globalSerOpts.databits = DEFAULT_SEROPT_DATABITS;
     conf->globalSerOpts.parity = DEFAULT_SEROPT_PARITY;
     conf->globalSerOpts.stopbits = DEFAULT_SEROPT_STOPBITS;
+#ifdef WITH_FREEIPMI
+    conf->globalIpmiOpts.username = NULL;
+    conf->globalIpmiOpts.password = NULL;
+    memset(conf->globalIpmiOpts.k_g, 0, IPMI_K_G_MAX);
+#endif /* WITH_FREEIPMI */
     conf->enableKeepAlive = 1;
     conf->enableLoopBack = 0;
     conf->enableTCPWrap = 0;
@@ -304,7 +321,10 @@ void destroy_server_conf(server_conf_t *conf)
     destroy_string(conf->logFmtName);
     destroy_string(conf->pidFileName);
     destroy_string(conf->resetCmd);
-
+#ifdef WITH_FREEIPMI
+    destroy_string(conf->globalIpmiOpts.username);
+    destroy_string(conf->globalIpmiOpts.password);
+#endif /* WITH_FREEIPMI */
     free(conf);
     return;
 }
@@ -563,6 +583,9 @@ static void signal_daemon(server_conf_t *conf)
         fprintf(stderr, "Configuration \"%s\" (pid %d) %s signal=%d\n",
             conf->confFileName, (int) pid, msg, conf->throwSignal);
     }
+#ifdef WITH_FREEIPMI
+    ipmi_teardown();
+#endif /* WITH_FREEIPMI */
     destroy_server_conf(conf);
     exit(0);
 }
@@ -571,7 +594,8 @@ static void signal_daemon(server_conf_t *conf)
 static void parse_console_directive(server_conf_t *conf, Lex l)
 {
 /*  CONSOLE NAME="<str>" DEV="<file>" \
- *    [LOG="<file>"] [LOGOPTS="<str>"] [SEROPTS="<str>"]
+ *    [LOG="<file>"] [LOGOPTS="<str>"] [SEROPTS="<str>"] [IPMIOPTS="<str>"]
+ *  note: IPMIOPTS is only available if configured with WITH_FREEIPMI
  */
     char *directive;                    /* name of directive being parsed */
     int line;                           /* line # where directive begins */
@@ -676,7 +700,22 @@ static void parse_console_directive(server_conf_t *conf, 
Lex l)
                 replace_string(&con.sopts, lex_text(l));
             }
             break;
-
+#ifdef WITH_FREEIPMI
+       case SERVER_CONF_IPMIOPTS:
+           if (lex_next(l) != '=') {
+               snprintf(err, sizeof(err), "unexpected '=' after %s keyword",
+                    server_conf_strs[LEX_UNTOK(tok)]);
+           }
+           else if ((lex_next(l) != LEX_STR)
+                    || is_empty_string(lex_text(l))) {
+               snprintf(err, sizeof(err), "expected STRING for %s value",
+                   server_conf_strs[LEX_UNTOK(tok)]);
+           }
+           else {
+               replace_string(&con.iopts, lex_text(l));
+           }
+           break;
+#endif /* WITH_FREEIPMI */
         case LEX_EOF:
         case LEX_EOL:
             done = 1;
@@ -730,6 +769,9 @@ static int process_console(server_conf_t *conf, 
console_strs_t *con_p,
     char        *path = NULL;
     obj_t       *console;
     seropt_t     seropts;
+#ifdef WITH_FREEIPMI
+    ipmiopt_t    ipmiopts;
+#endif /* WITH_FREEIPMI */
     logopt_t     logopts;
     obj_t       *logfile;
 
@@ -813,6 +855,26 @@ static int process_console(server_conf_t *conf, 
console_strs_t *con_p,
             goto err;
         }
     }
+#ifdef WITH_FREEIPMI
+    else if (is_ipmi_dev(arg0, &host)) {
+       if (list_count(args) != 1) {
+           snprintf(errbuf, errbuflen,
+               "console [%s] dev string has too many args", con_p->name);
+           goto err;
+       }
+       ipmiopts = conf->globalIpmiOpts;
+       if (con_p->iopts && parse_ipmi_opts(
+               &ipmiopts, con_p->iopts, errbuf, errbuflen) < 0) {
+           goto err;
+       }
+       if (!(console = create_ipmi_obj(
+               conf, con_p->name, &ipmiopts, host, errbuf, errbuflen))) {
+           goto err;
+       }
+       free(host);
+       host = NULL;
+    }
+#endif /* WITH_FREEIPMI */
     else {
         snprintf(errbuf, errbuflen,
             "console [%s] device \"%s\" type unrecognized",
@@ -884,7 +946,30 @@ static int is_telnet_dev(const char *dev, char 
**host_ref, int *port_ref)
     }
     return(1);
 }
+#ifdef WITH_FREEIPMI
+static int is_ipmi_dev(const char *dev, char **host_ref)
+{
+    char  buf[MAX_LINE];
+    char *p;
 
+    assert(dev != NULL);
+
+    if (strlcpy(buf, dev, sizeof(buf)) >= sizeof(buf)) {
+        return(0);
+    }
+    if (strncmp(buf, "ipmi:", 5) != 0) {
+       return(0);
+    }
+    p = buf + 5;
+    if (p == '\0') {
+       return(0);
+    }
+    if (host_ref) {
+       *host_ref = strdup(p);
+    }
+    return(1);
+}
+#endif /* WITH_FREEIPMI */
 
 static int is_serial_dev(const char *dev, const char *cwd, char **path_ref)
 {
@@ -1083,7 +1168,23 @@ static void parse_global_directive(server_conf_t 
*conf, Lex l)
                     err, sizeof(err));
             }
             break;
-
+#ifdef WITH_FREEIPMI
+       case SERVER_CONF_IPMIOPTS:
+            if (lex_next(l) != '=') {
+                snprintf(err, sizeof(err), "expected '=' after %s keyword",
+                    server_conf_strs[LEX_UNTOK(tok)]);
+            }
+            else if ((lex_next(l) != LEX_STR)
+                    || is_empty_string(lex_text(l))) {
+                snprintf(err, sizeof(err), "expected STRING for %s value",
+                    server_conf_strs[LEX_UNTOK(tok)]);
+            }
+            else {
+                parse_ipmi_opts(&conf->globalIpmiOpts, lex_text(l),
+                    err, sizeof(err));
+            }
+            break;
+#endif /* WITH_FREEIPMI */
         case LEX_EOF:
         case LEX_EOL:
             done = 1;
diff --git a/server-esc.c b/server-esc.c
index 76981ce..cb19d2c 100644
--- a/server-esc.c
+++ b/server-esc.c
@@ -169,6 +169,11 @@ static void perform_serial_break(obj_t *client)
         else if (is_telnet_obj(console)) {
             send_telnet_cmd(console, BREAK, -1);
         }
+#ifdef WITH_FREEIPMI
+       else if (is_ipmi_obj(console)) {
+           send_ipmi_break(console);
+       }
+#endif /* WITH_FREEIPMI */
     }
     list_iterator_destroy(i);
     return;
diff --git a/server-ipmi.c b/server-ipmi.c
new file mode 100644
index 0000000..504e080
--- /dev/null
+++ b/server-ipmi.c
@@ -0,0 +1,483 @@
+/*****************************************************************************
+ *  $Id$
+ 
*****************************************************************************
+ *  Written by Levi Pearson <lpearson@xxxxxxxx>.
+ *
+ *  This file is part of ConMan: The Console Manager.
+ *  For details, see <http://home.gna.org/conman/>.
+ *
+ *  This is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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 General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ 
*****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <assert.h>
+#include <ipmiconsole.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include "list.h"
+#include "server.h"
+#include "common.h"
+#include "tpoll.h"
+#include "util-str.h"
+
+static int ipmi_engine_started = 0;
+extern tpoll_t tp_global;              /* defined in server.c */
+
+static int connect_ipmi_obj(obj_t *ipmi);
+static void disconnect_ipmi_obj(obj_t *ipmi);
+
+void ipmi_setup(void) {
+    if (ipmi_engine_started == 0) {
+       ipmiconsole_engine_init(IPMI_ENGINE_THREADS, 0);
+       ipmi_engine_started = 1;
+    }
+}
+
+void ipmi_teardown(void) {
+    if (ipmi_engine_started == 1) {
+       ipmiconsole_engine_teardown();
+       ipmi_engine_started = 0;
+    }
+}
+
+/* a k_g key is interpreted as ascii text unless it is prefixed with
+   "0x", in which case is it interpreted as hexadecimal */
+static int parse_kg(unsigned char *outbuf, int outsz, const char *instr)
+{
+  char *p, *q;
+  int i, j;
+  char buf[3] = {0, 0, 0};
+
+  assert(outbuf != NULL);
+  assert(instr != NULL);
+  assert(outsz == IPMI_K_G_MAX);
+
+  if (strlen(instr) == 0)
+      return(0);
+
+  if (strncmp(instr, "0x", 2) == 0) {
+      if (strlen(instr) > IPMI_K_G_MAX*2+2)
+         return(-1);
+      p = (char *)instr + 2;
+      memset(outbuf, 0, IPMI_K_G_MAX);
+      for (i = j = 0; i < strlen(p); i+=2, j++) {
+          if (p[i+1] == '\0')
+             return(-1);
+          buf[0] = p[i]; buf[1] = p[i+1]; buf[2] = 0;
+          errno = 0;
+          outbuf[j] = strtoul(buf, &q, 16);
+          if (errno || (q != buf + 2))
+             return(-1);
+      }
+  }
+  else {
+      if (strlen(instr) > IPMI_K_G_MAX)
+         return(-1);
+      memset(outbuf, 0, IPMI_K_G_MAX);
+      memcpy(outbuf, instr, strlen(instr));
+  }
+
+  return(1);
+}
+
+static char *format_kg(char *outstr, int outsz, const unsigned char *k_g)
+{
+  int i;
+  int printable = 1;
+  int foundnull = 0;
+  char *p;
+
+  assert(outstr != NULL);
+  assert(outsz > IPMI_K_G_MAX*2+2);
+  assert(k_g != NULL);
+
+  /* Are there any characters that would prevent printing this as a
+     string on a single line? */
+  for (i = 0; i < IPMI_K_G_MAX; i++) {
+      if (k_g[i] == '\0') {
+          ++foundnull;
+          continue;
+      }
+      if (!(isgraph(k_g[i]) || k_g[i] == ' ') || foundnull) {
+          printable = 0;
+          break;
+      }
+  }
+
+  /* print out an entirely null key in hex rather than an empty
+     string */
+  if (foundnull == IPMI_K_G_MAX)
+      printable = 0;
+
+  /* don't print out a key starting with a literal '0x' as a string,
+     since parse_kg will try to interpret such strings as hex */
+  if (k_g[0] == '0' && k_g[1] == 'x')
+      printable = 0;
+
+  if (printable) {
+      if (outsz < IPMI_K_G_MAX+1)
+         return(NULL);
+      p = outstr;
+      for (i = 0; i < IPMI_K_G_MAX; i++) {
+          if (k_g[i] == '\0')
+             break;
+          p[i] = k_g[i];
+      }
+      p[i] = 0;
+  }
+  else {
+      if (outsz < IPMI_K_G_MAX*2+3)
+         return(NULL);
+      p = outstr;
+      p[0] = '0'; p[1] = 'x';
+      p += 2;
+      for (i = 0; i < IPMI_K_G_MAX; i++, p+=2)
+         sprintf(p, "%02x", k_g[i]);
+  }
+
+  return(outstr);
+}
+
+int parse_ipmi_opts(
+    ipmiopt_t *iopts, const char *str, char *errbuf, int errlen)
+{
+/*  Parses 'str' for ipmi device options 'iopts'.
+ *    The 'iopts' struct should be initialized to a default value.
+ *    The 'str' string is of the form "<username>,<password>[,<K_g key>]".
+ *  Returns 0 and updates the 'iopts' struct on sucess; o/w, returns -1
+ *    (writing an error message into 'errbuf' if defined).
+ */
+    ipmiopt_t ioptsTmp;
+    char buf[MAX_LINE];
+    const char * const separators = ",";
+    char *tok;
+
+    assert(iopts != NULL);
+
+    memset(&ioptsTmp, 0, sizeof(ipmiopt_t));
+
+    if ((str == NULL) || str[0] == '\0') {
+       if ((errbuf != NULL) && (errlen > 0))
+           snprintf(errbuf, errlen, "encountered empty options string");
+       return(-1);
+    }
+    
+    if (strlcpy(buf, str, sizeof(buf)) >= sizeof(buf)) {
+       if ((errbuf != NULL) && (errlen > 0))
+           snprintf(errbuf, errlen, "ipmiopt string exceeded buffer size");
+       return(-1);
+    }
+   
+    tok = strtok(buf, separators);
+    if (tok == NULL) {
+       if ((errbuf != NULL) && (errlen > 0))
+           snprintf(errbuf, errlen, "ipmiopt username not specified");
+       return(-1);
+    }
+    if (strlen(tok) > IPMI_USERNAME_MAX) {
+       if ((errbuf != NULL) && (errlen > 0))
+           snprintf(errbuf, errlen, "ipmiopt username exceeds max length");
+       return(-1);
+    }
+
+    ioptsTmp.username = strdup(tok);
+
+    tok = strtok(NULL, separators);
+    if (tok == NULL) {
+       if ((errbuf != NULL) && (errlen > 0))
+           snprintf(errbuf, errlen, "ipmiopt password not specified");
+       goto cleanup;
+    }
+    if (strlen(tok) > IPMI_PASSWORD_MAX) {
+       if ((errbuf != NULL) && (errlen > 0))
+           snprintf(errbuf, errlen, "ipmiopt password exceeds max length");
+       goto cleanup;
+    }
+
+    ioptsTmp.password = strdup(tok);
+
+    tok = strtok(NULL, separators);
+    if (tok == NULL) {
+       memcpy(ioptsTmp.k_g, iopts->k_g, IPMI_K_G_MAX);
+    } 
+    else {
+       if (parse_kg(ioptsTmp.k_g, IPMI_K_G_MAX, tok) < 0) {
+           if ((errbuf != NULL && (errlen > 0)))
+               snprintf(errbuf, errlen, "ipmiopt k_g is invalid");
+           goto cleanup;
+       }
+    }
+    
+    *iopts = ioptsTmp;
+    return(0);
+
+ cleanup:
+    if (ioptsTmp.username)
+       free(ioptsTmp.username);
+    if (ioptsTmp.password)
+       free(ioptsTmp.password);
+    return(-1);
+}
+
+obj_t * create_ipmi_obj(server_conf_t *conf, char *name,
+    ipmiopt_t *iconf, char *hostname, char *errbuf, int errlen)
+{
+/*  Creates a new ipmi device object and adds it to the master objs list.
+ *  Returns the new object, or NULL on error.
+ */
+    ListIterator i;
+    obj_t *ipmi;
+
+    assert(conf != NULL);
+    assert((name != NULL) && (name[0] != '\0'));
+    assert(iconf != NULL);
+
+    /*  Check for duplicate console names.
+     */
+    i = list_iterator_create(conf->objs);
+    while ((ipmi = list_next(i))) {
+        if (is_console_obj(ipmi) && !strcmp(ipmi->name, name)) {
+           snprintf(errbuf, errlen,
+               "console [%s] specifies duplicate console name", name);
+           break;
+       }
+       if (is_ipmi_obj(ipmi) && !strcmp(ipmi->aux.ipmi.hostname, hostname)) {
+           snprintf(errbuf, errlen,
+                    "console [%s] specifies duplicate hostname \"%s\"", 
+                    name, hostname);
+           break;
+       }
+    }
+    list_iterator_destroy(i);
+    if (ipmi != NULL) {
+       return(NULL);
+    }
+    ipmi = create_obj(conf, name, -1, CONMAN_OBJ_IPMI);
+    ipmi->aux.ipmi.hostname = strdup(hostname);
+    ipmi->aux.ipmi.iconf = *iconf;
+    ipmi->aux.ipmi.ctx = NULL;
+    ipmi->aux.ipmi.logfile = NULL;
+    ipmi->aux.ipmi.state = CONMAN_IPMI_DOWN;
+    /*  Add obj to the master conf->objs list.
+     */
+    list_append(conf->objs, ipmi);
+
+    return(ipmi);
+}
+
+int new_ipmi_ctx(obj_t *ipmi)
+{
+/*
+ *  Returns 0 if the IPMI ctx is sucessfully created; o/w, returns -1.
+ */
+    struct ipmiconsole_ipmi_config ipmi_config;
+    struct ipmiconsole_protocol_config protocol_config;
+
+    assert(ipmi != NULL);
+    assert(is_ipmi_obj(ipmi));
+
+    /* create the ctx object if it doesn't already exist */
+    if (ipmi->aux.ipmi.ctx == NULL) {
+       /* set up configuration structs for the ctx creation */
+       ipmi_config.username = ipmi->aux.ipmi.iconf.username;
+       ipmi_config.password = ipmi->aux.ipmi.iconf.password;
+       ipmi_config.k_g = malloc(IPMI_K_G_MAX);
+       memcpy(ipmi_config.k_g, ipmi->aux.ipmi.iconf.k_g, IPMI_K_G_MAX);
+
+       ipmi_config.k_g_len = IPMI_K_G_MAX;
+       ipmi_config.privilege_level = -1;
+       ipmi_config.cipher_suite_id = -1;
+
+       protocol_config.session_timeout_len = -1;
+       protocol_config.retransmission_timeout_len = -1;
+       protocol_config.retransmission_backoff_count = -1;
+       protocol_config.keepalive_timeout_len = -1;
+       protocol_config.retransmission_keepalive_timeout_len = -1;
+       protocol_config.acceptable_packet_errors_count = -1;
+       protocol_config.maximum_retransmission_count = -1;
+       protocol_config.debug_flags = 0;
+       protocol_config.security_flags = 0;
+       protocol_config.workaround_flags = 0;
+
+       ipmi->aux.ipmi.ctx = ipmiconsole_ctx_create(ipmi->aux.ipmi.hostname,
+                                                   &ipmi_config,
+                                                   &protocol_config);
+       if (!ipmi->aux.ipmi.ctx)
+           return(-1);
+
+       ipmi->aux.ipmi.state = CONMAN_IPMI_DOWN;
+    }
+    return(0);
+}
+
+int open_ipmi_obj(obj_t *ipmi)
+{
+/*  (Re)opens the specified 'ipmi' obj.
+ *  Returns 0 if the IPMI console is sucessfully opened; o/w, returns -1.
+ */
+    int rv = 0;
+    assert(is_ipmi_obj(ipmi));
+
+    if (ipmi->aux.ipmi.state == CONMAN_IPMI_UP) {
+       disconnect_ipmi_obj(ipmi);
+    }
+    else {
+       rv = new_ipmi_ctx(ipmi);
+       if (rv < 0)
+           return(rv);
+       rv = connect_ipmi_obj(ipmi);
+    }
+    DPRINTF((9, "Opened [%s] ipmi: host=%s state=%d.\n",
+            ipmi->name, ipmi->aux.ipmi.hostname, ipmi->fd, (int) 
ipmi->aux.ipmi.state));
+    return(rv);
+}
+
+int send_ipmi_break(obj_t *ipmi)
+{
+    assert(ipmi != NULL);
+    assert(is_ipmi_obj(ipmi));
+    assert(ipmi->aux.ipmi.ctx != NULL);
+    return(ipmiconsole_ctx_generate_break(ipmi->aux.ipmi.ctx));
+}
+
+static int connect_ipmi_obj(obj_t *ipmi) 
+{
+    int rv = 0;
+
+    assert(ipmi != NULL);
+    assert(is_ipmi_obj(ipmi));
+    assert(ipmi->aux.ipmi.state != CONMAN_IPMI_UP);
+    assert(ipmi->aux.ipmi.ctx != NULL);
+
+    if (ipmi->aux.ipmi.timer >= 0) {
+       (void) tpoll_timeout_cancel(tp_global, ipmi->aux.ipmi.timer);
+       ipmi->aux.ipmi.timer = -1;
+    }
+    if (ipmi->aux.ipmi.state == CONMAN_IPMI_DOWN) {
+       /* Do a non-blocking submit of this ctx to the engine */
+       DPRINTF((10, "Connecting to <%s> for [%s].\n", 
+                ipmi->aux.ipmi.hostname, ipmi->name));
+       rv = ipmiconsole_engine_submit(ipmi->aux.ipmi.ctx);
+       if (rv < 0) {
+           log_msg(LOG_WARNING, 
+                   "Unable to submit ipmi ctx to engine for [%s]: %s", 
ipmi->name,
+                   
ipmiconsole_ctx_strerror(ipmiconsole_ctx_errnum(ipmi->aux.ipmi.ctx)));
+           return(-1);
+       }
+       if ((ipmi->fd = ipmiconsole_ctx_fd(ipmi->aux.ipmi.ctx)) < 0) {
+           log_msg(LOG_WARNING, 
+                   "Unable to get a file descriptor for [%s]", ipmi->name);
+           return(-1);
+       }
+       ipmi->aux.ipmi.state = CONMAN_IPMI_PENDING;
+       ipmi->aux.ipmi.timer = tpoll_timeout_relative(tp_global,
+                                                     (callback_f) 
connect_ipmi_obj,
+                                                     ipmi, 
+                                                     
IPMI_STATUS_CHECK_TIMEOUT * 1000);
+       return(-1);
+    }
+    else if (ipmi->aux.ipmi.state == CONMAN_IPMI_PENDING) {
+       /* Check with the engine to see if the connection was successful */
+       rv = ipmiconsole_ctx_status(ipmi->aux.ipmi.ctx);
+       if (rv < 0) {
+           /* something wonky happened and we couldn't get the connection 
status */
+           log_msg(LOG_WARNING, 
+                   "Error retrieving ipmi ctx status from engine for [%s]", 
ipmi->name);
+           disconnect_ipmi_obj(ipmi);
+           return(-1);
+       }
+
+       if (rv == IPMICONSOLE_CONTEXT_STATUS_ERROR) {
+           /* an error occurred with the ipmi connection */
+           rv = ipmiconsole_ctx_errnum(ipmi->aux.ipmi.ctx);
+           log_msg(LOG_WARNING, "Error establishing ipmi link for [%s]: %s", 
+                   ipmi->name,
+                   ipmiconsole_ctx_strerror(rv));
+           ipmi->fd = -1;
+           disconnect_ipmi_obj(ipmi);
+           return(-1);
+       }
+
+       if (rv == IPMICONSOLE_CONTEXT_STATUS_NONE) {
+           /* wait a bit longer */
+           ipmi->aux.ipmi.timer = tpoll_timeout_relative(tp_global,
+                                                         (callback_f) 
connect_ipmi_obj,
+                                                         ipmi, 
+                                                         
IPMI_STATUS_CHECK_TIMEOUT * 1000);
+           return(-1);
+       }
+       
+       if (rv == IPMICONSOLE_CONTEXT_STATUS_SOL_ESTABLISHED) {
+           /* success! */
+           write_notify_msg(ipmi, LOG_INFO, "Console [%s] connected to <%s> 
via IPMI",
+                            ipmi->name, ipmi->aux.ipmi.hostname);
+           ipmi->aux.ipmi.state = CONMAN_IPMI_UP;
+           return(0);
+       }
+       else {
+           log_msg(LOG_WARNING, 
+                   "Unrecognized ipmi ctx status value %d for [%s]", rv, 
ipmi->name);
+           disconnect_ipmi_obj(ipmi);
+           return(-1);
+       }
+    }
+    return(rv);
+}
+
+static void disconnect_ipmi_obj(obj_t *ipmi) 
+{
+    assert(ipmi != NULL);
+    assert(is_ipmi_obj(ipmi));
+    int rv;
+
+    if (ipmi->aux.ipmi.timer >= 0) {
+       (void) tpoll_timeout_cancel(tp_global, ipmi->aux.ipmi.timer);
+       ipmi->aux.ipmi.timer = -1;
+    }
+    if (ipmi->fd >= 0) {
+       close(ipmi->fd);
+       ipmi->fd = -1;
+    }
+    if (ipmiconsole_ctx_destroy(ipmi->aux.ipmi.ctx) < 0) {
+       ipmi->aux.ipmi.timer = tpoll_timeout_relative(tp_global,
+                                                     (callback_f) 
disconnect_ipmi_obj,
+                                                     ipmi,
+                                                     
IPMI_STATUS_CHECK_TIMEOUT * 1000);
+    }
+    else {
+       ipmi->aux.ipmi.ctx = NULL;
+       if (ipmi->aux.ipmi.state == CONMAN_IPMI_UP) {
+           write_notify_msg(ipmi, LOG_NOTICE,
+                            "Console [%s] disconnected from <%s> via IPMI",
+                            ipmi->name, ipmi->aux.ipmi.hostname);
+       }
+       rv = new_ipmi_ctx(ipmi);
+       if (rv < 0)
+           log_err(rv, "new_ipmi_ctx failed");
+       ipmi->aux.ipmi.state = CONMAN_IPMI_DOWN;
+       ipmi->aux.ipmi.timer = tpoll_timeout_relative(tp_global,
+                                                     (callback_f) 
connect_ipmi_obj,
+                                                     ipmi,
+                                                     
IPMI_CONNECT_RETRY_TIMEOUT * 1000);
+    }
+}
+
diff --git a/server-logfile.c b/server-logfile.c
index f59fffe..e46fceb 100644
--- a/server-logfile.c
+++ b/server-logfile.c
@@ -178,6 +178,11 @@ obj_t * create_logfile_obj(server_conf_t *conf, char 
*name,
     else if (is_telnet_obj(console)) {
         console->aux.telnet.logfile = logfile;
     }
+#ifdef WITH_FREEIPMI
+    else if (is_ipmi_obj(console)) {
+       console->aux.ipmi.logfile = logfile;
+    }
+#endif /* WITH_FREEIPMI */
     else {
         log_err(0, "INTERNAL: Unrecognized console [%s] type=%d",
             console->name, console->type);
@@ -301,6 +306,11 @@ obj_t * get_console_logfile_obj(obj_t *console)
     else if (is_telnet_obj(console)) {
         logfile = console->aux.telnet.logfile;
     }
+#ifdef WITH_FREEIPMI
+    else if (is_ipmi_obj(console)) {
+        logfile = console->aux.ipmi.logfile;
+    }
+#endif /* WITH_FREEIPMI */
     else {
         log_err(0, "INTERNAL: Unrecognized console [%s] type=%d",
             console->name, console->type);
diff --git a/server-obj.c b/server-obj.c
index 964910b..1a7225b 100644
--- a/server-obj.c
+++ b/server-obj.c
@@ -38,6 +38,9 @@
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
+#ifdef WITH_FREEIPMI
+#include <ipmiconsole.h>
+#endif /* WITH_FREEIPMI */
 #include "common.h"
 #include "list.h"
 #include "log.h"
@@ -68,7 +71,10 @@ obj_t * create_obj(
         || type==CONMAN_OBJ_LOGFILE
         || type==CONMAN_OBJ_PROCESS
         || type==CONMAN_OBJ_SERIAL
-        || type==CONMAN_OBJ_TELNET);
+#ifdef WITH_FREEIPMI
+       || type==CONMAN_OBJ_IPMI
+#endif /* WITH_FREEIPMI */
+       || type==CONMAN_OBJ_TELNET);
 
     if (!(obj = malloc(sizeof(obj_t))))
         out_of_memory();
@@ -197,6 +203,19 @@ void destroy_obj(obj_t *obj)
         /*  Do not destroy obj->aux.telnet.logfile since it is only a ref.
          */
         break;
+#ifdef WITH_FREEIPMI
+    case CONMAN_OBJ_IPMI:
+       if (obj->aux.ipmi.hostname) {
+           free(obj->aux.ipmi.hostname);
+       }
+       if (obj->aux.ipmi.ctx) {
+           while (ipmiconsole_ctx_destroy(obj->aux.ipmi.ctx) < 0) {
+               printf("%s\n", 
ipmiconsole_ctx_strerror(ipmiconsole_ctx_errnum(obj->aux.ipmi.ctx)));
+               sleep(1);
+           }
+       }
+       break;
+#endif /* WITH_FREEIPMI */
     default:
         log_err(0, "INTERNAL: Unrecognized object [%s] type=%d",
             obj->name, obj->type);
@@ -239,6 +258,12 @@ void reopen_obj(obj_t *obj)
     else if (is_telnet_obj(obj)) {
         open_telnet_obj(obj);
     }
+#ifdef WITH_FREEIPMI
+    else if (is_ipmi_obj(obj)) {
+       ipmi_setup();
+       open_ipmi_obj(obj);
+    }
+#endif /* WITH_FREEIPMI */
     else if (is_client_obj(obj)) {
         ; /* no-op */
     }
diff --git a/server-sock.c b/server-sock.c
index a9ead8c..49cd12d 100644
--- a/server-sock.c
+++ b/server-sock.c
@@ -921,5 +921,19 @@ static void check_console_state(obj_t *console, obj_t 
*client)
             open_telnet_obj(console);
         }
     }
+#ifdef WITH_FREEIPMI
+    else if (is_ipmi_obj(console)
+            && (console->aux.ipmi.state != CONMAN_IPMI_UP)) {
+        snprintf(buf, sizeof(buf),
+            "%sConsole [%s] is currently disconnected from <%s>%s",
+            CONMAN_MSG_PREFIX, console->name, console->aux.ipmi.hostname,
+            CONMAN_MSG_SUFFIX);
+        strcpy(&buf[sizeof(buf) - 3], "\r\n");
+        write_obj_data(client, buf, strlen(buf), 1);
+        if (console->aux.ipmi.state == CONMAN_IPMI_DOWN) {
+            open_ipmi_obj(console);
+        }
+    }
+#endif /* WITH_FREEIPMI */
     return;
 }
diff --git a/server.c b/server.c
index 5daba19..bc405b6 100644
--- a/server.c
+++ b/server.c
@@ -144,6 +144,9 @@ int main(int argc, char *argv[])
 
     open_objs(conf);
     mux_io(conf);
+#ifdef WITH_FREEIPMI
+    ipmi_teardown();
+#endif /* WITH_FREEIPMI */
     destroy_server_conf(conf);
 
     if (pgid > 0) {
@@ -665,6 +668,10 @@ static void mux_io(server_conf_t *conf)
             }
             if ((is_telnet_obj(obj)
                 && obj->aux.telnet.conState == CONMAN_TELCON_UP)
+#ifdef WITH_FREEIPMI
+             ||(is_ipmi_obj(obj)
+               && obj->aux.ipmi.state == CONMAN_IPMI_UP)
+#endif /* WITH_FREEIPMI */
               || is_serial_obj(obj)
               || is_process_obj(obj)
               || is_client_obj(obj)) {
diff --git a/server.h b/server.h
index d7c3d08..86302ce 100644
--- a/server.h
+++ b/server.h
@@ -34,6 +34,9 @@
 #include <sys/types.h>
 #include <termios.h>                    /* for struct termios, speed_t       
*/
 #include <time.h>                       /* for time_t                        
*/
+#ifdef WITH_FREEIPMI
+#include <ipmiconsole.h>                /* for IPMI SoL consoles             
*/
+#endif /* WITH_FREEIPMI */
 #include "common.h"
 #include "list.h"
 #include "tpoll.h"
@@ -57,12 +60,24 @@
 #define TELNET_MAX_TIMEOUT              1800
 #define TELNET_MIN_TIMEOUT              15
 
+#ifdef WITH_FREEIPMI
+#define IPMI_K_G_MAX                    20
+#define IPMI_USERNAME_MAX               16
+#define IPMI_PASSWORD_MAX               20
+#define IPMI_ENGINE_THREADS             5
+#define IPMI_STATUS_CHECK_TIMEOUT       5 /* seconds */
+#define IPMI_CONNECT_RETRY_TIMEOUT      30 /* seconds */
+#endif /* WITH_FREEIPMI */
+
 
 enum obj_type {                         /* type of auxiliary obj (3 bits)    
*/
     CONMAN_OBJ_CLIENT,
     CONMAN_OBJ_LOGFILE,
     CONMAN_OBJ_PROCESS,
     CONMAN_OBJ_SERIAL,
+#ifdef WITH_FREEIPMI
+    CONMAN_OBJ_IPMI,
+#endif /* WITH_FREEIPMI */
     CONMAN_OBJ_TELNET
 };
 
@@ -136,11 +151,39 @@ typedef struct telnet_obj {             /* TELNET AUX 
OBJ DATA:              */
     unsigned         enableKeepAlive:1; /*  true if using TCP keep-alive     
*/
 } telnet_obj_t;
 
+#ifdef WITH_FREEIPMI
+typedef struct ipmi_opt {               /* IPMI OBJ OPTIONS:                 
*/
+    char            *username;          /*  BMC user name for auth           
*/
+    char            *password;          /*  BMC password for auth            
*/
+    unsigned char   k_g[IPMI_K_G_MAX];            /*  BMC Key for 2-key auth 
(optional)*/
+} ipmiopt_t;
+
+typedef struct ipmiconsole_ctx ipmictx_t;
+
+typedef enum ipmi_connect_state {
+    CONMAN_IPMI_DOWN,
+    CONMAN_IPMI_PENDING,
+    CONMAN_IPMI_UP
+} ipmi_state_t;
+
+typedef struct ipmi_obj {               /* IPMI AUX OBJ DATA:                
*/
+    char            *hostname;          /*  remote bmc host name/ip          
*/
+    ipmiopt_t        iconf;             /*  conf to connect to bmc           
*/
+    ipmictx_t       *ctx;               /*  ipmi session ctx obj             
*/
+    struct base_obj *logfile;           /*  log obj ref for console          
*/
+    ipmi_state_t     state;             /*  connection state                 
*/
+    int              timer;             /*  timer id                         
*/
+} ipmi_obj_t;
+#endif /* WITH_FREEIPMI */
+
 typedef union aux_obj {
     client_obj_t     client;
     logfile_obj_t    logfile;
     process_obj_t    process;
     serial_obj_t     serial;
+#ifdef WITH_FREEIPMI
+    ipmi_obj_t       ipmi;
+#endif /* WITH_FREEIPMI */
     telnet_obj_t     telnet;
 } aux_obj_t;
 
@@ -183,6 +226,9 @@ typedef struct server_conf {
     char            *globalLogName;     /* global log name (must contain &)  
*/
     logopt_t         globalLogOpts;     /* global opts for logfile objects   
*/
     seropt_t         globalSerOpts;     /* global opts for serial objects    
*/
+#ifdef WITH_FREEIPMI
+    ipmiopt_t        globalIpmiOpts;    /* global opts for ipmi objects      
*/
+#endif /* WITH_FREEIPMI */
     unsigned         enableKeepAlive:1; /* true if using TCP keep-alive      
*/
     unsigned         enableLoopBack:1;  /* true if only listening on 
loopback*/
     unsigned         enableTCPWrap:1;   /* true if TCP-Wrappers is enabled   
*/
@@ -238,8 +284,15 @@ typedef struct client_args {
 #define is_process_obj(OBJ)  (OBJ->type == CONMAN_OBJ_PROCESS)
 #define is_serial_obj(OBJ)   (OBJ->type == CONMAN_OBJ_SERIAL)
 #define is_telnet_obj(OBJ)   (OBJ->type == CONMAN_OBJ_TELNET)
+#ifdef WITH_FREEIPMI
+#define is_ipmi_obj(OBJ)     (OBJ->type == CONMAN_OBJ_IPMI)
 #define is_console_obj(OBJ)  \
+    (is_process_obj(OBJ) || is_serial_obj(OBJ) || \
+     is_telnet_obj(OBJ) || is_ipmi_obj(OBJ))
+#else /* !WITH_FREEIPMI */
+#define is_console_obj(OBJ) \
     (is_process_obj(OBJ) || is_serial_obj(OBJ) || is_telnet_obj(OBJ))
+#endif /* WITH_FREEIPMI */
 
 
 /*  server-conf.c
@@ -346,5 +399,22 @@ int process_telnet_escapes(obj_t *telnet, void *src, int 
len);
 
 int send_telnet_cmd(obj_t *telnet, int cmd, int opt);
 
+#ifdef WITH_FREEIPMI
+/* server-ipmi.c
+ */
+
+void ipmi_setup(void);
+void ipmi_teardown(void);
+
+int parse_ipmi_opts(
+    ipmiopt_t *iopts, const char *str, char *errbuf, int errlen);
+
+obj_t * create_ipmi_obj(server_conf_t *conf, char *name,
+    ipmiopt_t *iconf, char *hostname, char *errbuf, int errlen);
+
+int open_ipmi_obj(obj_t *ipmi);
+
+int send_ipmi_break(obj_t *ipmi);
+#endif /* WITH_FREEIPMI */
 
 #endif /* !_SERVER_H */

Related Messages


Powered by MHonArc, Updated Fri Aug 03 23:00:25 2007