[Date Prev] [Date Index] [Date Next] [Thread Prev] [Thread Index] [Thread Next]

Re: Moxa NPort 6150/6250

Anton D. Kachalov mouse@yandex-team.ru
Mon, 21 Nov 2011 07:30:55 GMT


I have a patch for conserver that introduces support for Moxa RealCOM TTY including SSL support.
Due to original code license (kernel module and user-space daemon), it coulnd't be commited to the conserver's mainstream. Moxa didn't permit to distribute the patch under BSD terms.
 
Usage:
console nport {
   master localhost;
   type realcom;
   host nport.somewhere.com;
   secured yes;
   portbase 950;
   portinc 16;
   port 7;
}
 
It's tested with NP6610-32.
18.11.2011, 14:03, "Wawrzek Niewodniczanski" <wawrzek.niewodniczanski@citrix.com>:

On 11/17/11 21:22, Joe Digilio wrote:

 Hi all-
 I'm a long time conserver user, and need to replace a failed Cyclades TS400.

 The Moxa terminal servers were recommended to me by a colleague (who
 does not use it with conserver).  I'm wondering if anybody has
 experience using them with conserver?  I'll likely get the 6150 or
 6250, since I only need a single port.

We have bigger Moxas (16 port ones) and they work fine with Conserver.

 I was also looking at the OpenGear ACM5002, if anybody wants to comment on that.

We had some Moxa failure this year and no response from support, so we
had to throw them away. On the other hand we have very good contact with
OpenGear. So it's not surprise that I would personally choose OpenGear.
Thanks,
Wawrzek

--
Wawrzyniec (Wawrzek) Niewodniczański - 01223 435603 (35603) - wawrzekn
System Administrator - Engineering Services Team (XenServer)
Citrix Systems, Building 101, Cambridge Science Park, CB4 0FY, Cambridge
PhD in Quantum Chemistry, MSc in Molecular Engineering

_______________________________________________
users mailing list
users@conserver.com
https://www.conserver.com/mailman/listinfo/users
--
Anton D. Kachalov
diff --git b/conserver/consent.c a/conserver/consent.c
index aae6c26..59dfc89 100644
--- b/conserver/consent.c
+++ a/conserver/consent.c
@@ -744,6 +744,7 @@ ConsDown(pCE, downHard, force)
 {
     if (force != FLAGTRUE &&
 	!(FileBufEmpty(pCE->fdlog) && FileBufEmpty(pCE->cofile) &&
+	/*  FileBufEmpty(pCE->cocmd) && */
 	  FileBufEmpty(pCE->initfile))) {
 	pCE->ioState = ISFLUSHING;
 	return;
@@ -762,6 +763,12 @@ ConsDown(pCE, downHard, force)
 	FD_CLR(cofile, &winit);
 	FileClose(&pCE->cofile);
     }
+    if (pCE->cocmd != (CONSFILE *)0) {
+	int cofile = FileFDNum(pCE->cocmd);
+	FD_CLR(cofile, &rinit);
+	FD_CLR(cofile, &winit);
+	FileClose(&pCE->cocmd);
+    }
     if (pCE->fdlog != (CONSFILE *)0) {
 	if (pCE->nolog) {
 	    TagLogfile(pCE, "Console logging restored");
@@ -799,6 +806,7 @@ ConsInit(pCE)
     time_t tyme;
     extern int FallBack PARAMS((char **, int *));
     int cofile = -1;
+    int cocmd = -1;
     int ret;
 #if HAVE_GETTIMEOFDAY
     struct timeval tv;
@@ -1129,6 +1137,147 @@ ipmi_sol_error:
 	    ipmi_intf_session_set_sol_escape_char(pCE->intf, SOL_ESCAPE_CHARACTER_DEFAULT);
 	    */
 	    break;
+
+	case REALCOM:
+	    {
+		struct sockaddr_in port;
+		struct hostent *hp;
+#if HAVE_SETSOCKOPT
+		int one = 1;
+#endif
+
+		usleep(100000);	/* Not all terminal servers can keep up */
+
+		if ((hp = gethostbyname(pCE->host)) == NULL) {
+		    Error("[%s] gethostbyname(%s): %s: forcing down",
+			  pCE->server, pCE->host, hstrerror(h_errno));
+		    ConsDown(pCE, FLAGTRUE, FLAGTRUE);
+		    return;
+		}
+#if HAVE_MEMCPY
+		memcpy(&port.sin_addr.s_addr, hp->h_addr_list[0],
+		       hp->h_length);
+#else
+		bcopy(hp->h_addr_list[0], &port.sin_addr.s_addr,
+		      hp->h_length);
+#endif
+		port.sin_family = hp->h_addrtype;
+		port.sin_port = htons(pCE->netport+16);
+
+		if ((cofile = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		    Error
+			("[%s] socket(AF_INET,SOCK_STREAM): %s: forcing down",
+			 pCE->server, strerror(errno));
+		    ConsDown(pCE, FLAGTRUE, FLAGTRUE);
+		    return;
+		}
+#if HAVE_SETSOCKOPT
+		if (setsockopt
+		    (cofile, SOL_SOCKET, SO_KEEPALIVE, (char *)&one,
+		     sizeof(one)) < 0) {
+		    Error
+			("[%s] setsockopt(%u,SO_KEEPALIVE): %s: forcing down",
+			 pCE->server, cofile, strerror(errno));
+		    ConsDown(pCE, FLAGTRUE, FLAGTRUE);
+		    close(cofile);
+		    return;
+		}
+#endif
+
+		if (!SetFlags(cofile, O_NONBLOCK, 0)) {
+		    ConsDown(pCE, FLAGTRUE, FLAGTRUE);
+		    close(cofile);
+		    return;
+		}
+
+		if ((ret =
+		     connect(cofile, (struct sockaddr *)&port,
+			     sizeof(port)))
+		    < 0) {
+		    if (errno != EINPROGRESS) {
+			Error("[%s] connect(%u): %s: forcing down",
+			      pCE->server, cofile, strerror(errno));
+			ConsDown(pCE, FLAGTRUE, FLAGTRUE);
+			close(cofile);
+			return;
+		    }
+		}
+		if ((pCE->cocmd =
+			 FileOpenFD(cofile, simpleSocket)) == (CONSFILE *)0) {
+			Error
+			    ("[%s] FileOpenFD(%d,simpleSocket) failed: forcing down",
+			     pCE->server, cofile);
+			ConsDown(pCE, FLAGTRUE, FLAGTRUE);
+			close(cofile);
+			return;
+		}
+		cocmd = cofile;
+
+		/* Opening data connection */
+		port.sin_port = htons(pCE->netport);
+
+		if ((cofile = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		    Error
+			("[%s] socket(AF_INET,SOCK_STREAM): %s: forcing down",
+			 pCE->server, strerror(errno));
+		    ConsDown(pCE, FLAGTRUE, FLAGTRUE);
+		    return;
+		}
+#if HAVE_SETSOCKOPT
+		if (setsockopt
+		    (cofile, SOL_SOCKET, SO_KEEPALIVE, (char *)&one,
+		     sizeof(one)) < 0) {
+		    Error
+			("[%s] setsockopt(%u,SO_KEEPALIVE): %s: forcing down",
+			 pCE->server, cofile, strerror(errno));
+		    ConsDown(pCE, FLAGTRUE, FLAGTRUE);
+		    close(cofile);
+		    return;
+		}
+#endif
+
+		if (!SetFlags(cofile, O_NONBLOCK, 0)) {
+		    ConsDown(pCE, FLAGTRUE, FLAGTRUE);
+		    close(cofile);
+		    return;
+		}
+
+		if ((ret =
+		     connect(cofile, (struct sockaddr *)&port,
+			     sizeof(port)))
+		    < 0) {
+		    if (errno != EINPROGRESS) {
+			Error("[%s] connect(%u): %s: forcing down",
+			      pCE->server, cofile, strerror(errno));
+			ConsDown(pCE, FLAGTRUE, FLAGTRUE);
+			close(cofile);
+			return;
+		    }
+		}
+		if ((pCE->cofile =
+			 FileOpenFD(cofile, pCE->secured == FLAGTRUE ? clientSSLSocket : simpleSocket)) == (CONSFILE *)0) {
+			Error
+			    ("[%s] FileOpenFD(%d,%s) failed: forcing down",
+			     pCE->server, cofile,
+			     pCE->secured ? "clientSSLSocket" : "simpleSocket");
+			ConsDown(pCE, FLAGTRUE, FLAGTRUE);
+			close(cofile);
+			return;
+		}
+
+	        if (ret == 0) {
+			pCE->ioState = ISNORMAL;
+			pCE->stateTimer = 0;
+	        } else {
+			pCE->ioState = INCONNECT;
+			pCE->stateTimer = time((time_t *)0) + CONNECTTIMEOUT;
+			if (timers[T_STATE] == (time_t)0 ||
+			    timers[T_STATE] > pCE->stateTimer)
+			    timers[T_STATE] = pCE->stateTimer;
+		}
+		pCE->fup = 1;
+	    }
+	    break;
     }
 
     if (!pCE->fup) {
@@ -1150,6 +1299,10 @@ ipmi_sol_error:
 	case IPMI:
 	    Verbose("[%s] on %s", pCE->server);
 	    break;
+	case REALCOM:
+	    Verbose("[%s] port %hu:%hu on %s", pCE->server,
+		    pCE->netport, pCE->netport+16, pCE->host);
+	    break;
 	case NOOP:
 	    Verbose("[%s] noop", pCE->server);
 	    break;
@@ -1174,6 +1327,18 @@ ipmi_sol_error:
 	    maxfd = cofile + 1;
     }
 
+    if (cocmd != -1) {
+	/* if we're waiting for connect() to finish, watch the
+	 * write bit, otherwise watch for the read bit
+	 */
+	if (pCE->ioState == INCONNECT)
+	    FD_SET(cocmd, &winit);
+	else
+	    FD_SET(cocmd, &rinit);
+	if (maxfd < cocmd + 1)
+	    maxfd = cocmd + 1;
+    }
+
     tyme = time((time_t *)0);
 
     if (pCE->ioState == ISNORMAL) {
diff --git b/conserver/consent.h a/conserver/consent.h
index 14c1338..ef36f8a 100644
--- b/conserver/consent.h
+++ a/conserver/consent.h
@@ -58,7 +58,8 @@ typedef enum consType {
     HOST,
     NOOP,
     UDS,
-    IPMI
+    IPMI,
+    REALCOM
 } CONSTYPE;
 
 typedef enum ipmiIntf {
@@ -97,6 +98,7 @@ typedef struct consent {	/* console information                  */
 #if defined(CRTSCTS)
     FLAG crtscts;		/* use hardware flow control            */
 #endif
+    /* type == REALCOM */
     FLAG secured;
 #if HAVE_OPENSSL
     SSL_CTX *ssl_ctx;
@@ -154,6 +156,7 @@ typedef struct consent {	/* console information                  */
     /*** runtime settings ***/
     CONSFILE *fdlog;		/* the local log file                   */
     CONSFILE *cofile;		/* the port to talk to machine on       */
+    CONSFILE *cocmd;		/* the command port to talk to          */
     char *execSlave;		/* pseudo-device slave side             */
     int execSlaveFD;		/* fd of slave side                     */
     pid_t ipid;			/* pid of virtual command               */
diff --git b/conserver/group.c a/conserver/group.c
index 017c78b..0e996df 100644
--- b/conserver/group.c
+++ a/conserver/group.c
@@ -2234,6 +2234,11 @@ CommandExamine(pGE, pCLServing, pCEServing, tyme, args)
 		b = "IPMI";
 		p = ' ';
 		break;
+	    case REALCOM:
+		d = BuildTmpStringPrint("%s/%hu", pCE->host, pCE->netport);
+		b = "RealC";
+		p = ' ';
+		break;
 	    case HOST:
 		d = BuildTmpStringPrint("%s/%hu", pCE->host, pCE->netport);
 		b = "Netwk";
@@ -2429,6 +2434,12 @@ CommandInfo(pGE, pCLServing, pCEServing, tyme, args)
 			  pCE->host,
 			  FileFDNum(pCE->cofile));
 		break;
+	    case REALCOM:
+		FilePrint(pCLServing->fd, FLAGTRUE, "!:%s,%hu,%d,%d:",
+			  pCE->host, pCE->netport,
+			  FileFDNum(pCE->cofile),
+			  FileFDNum(pCE->cocmd));
+		break;
 	    case HOST:
 		FilePrint(pCLServing->fd, FLAGTRUE, "!:%s,%hu,%s,%d:",
 			  pCE->host, pCE->netport,
@@ -2668,6 +2679,228 @@ TelOpt(o)
 
 void
 #if PROTOTYPES
+DoRealCOMPortInit(CONSENT *pCEServing)
+#else
+DoRealCOMPortInit(pCEServing)
+    CONSENT *pCEServing;
+#endif
+{
+    unsigned char cmd[10];
+    int  nr;
+    char baud;
+    int  mode;
+
+    int  cocmd = FileFDNum(pCEServing->cocmd);
+
+    mode = (pCEServing->cstopb == FLAGTRUE) ? ASPP_IOCTL_STOP2 : ASPP_IOCTL_STOP1;
+
+    if (pCEServing->parity != (PARITY *)0 && pCEServing->parity->iset & PARENB)
+    {
+	switch (pCEServing->parity->iset & CSIZE) {
+	case CS7:
+	    mode |= ASPP_IOCTL_BITS7;
+	    break;
+	case CS8:
+	default:
+	    mode |= ASPP_IOCTL_BITS8;
+	    break;
+        }
+#ifdef CMSPAR
+	if (pCEServing->parity->iset & CMSPAR)
+	    if (pCEServing->parity->iset & PARODD)
+		mode |= ASPP_IOCTL_MARK;
+	    else
+		mode |= ASPP_IOCTL_SPACE;
+	else
+#endif
+	    if (pCEServing->parity->iset & PARODD)
+		mode |= ASPP_IOCTL_ODD;
+	    else
+		mode |= ASPP_IOCTL_EVEN;
+    } else {
+	mode |= ASPP_IOCTL_BITS8;
+	mode |= ASPP_IOCTL_NONE;
+    }
+
+    if (pCEServing->baud == (BAUD *)0) {
+	baud = ASPP_IOCTL_B115200;
+    } else {
+	switch (pCEServing->baud->irate) {
+	case B921600:
+	    baud = ASPP_IOCTL_B921600;
+	    break;
+	case B460800:
+	    baud = ASPP_IOCTL_B460800;
+	    break;
+	case B230400:
+	    baud = ASPP_IOCTL_B230400;
+	    break;
+	case B115200:
+	    baud = ASPP_IOCTL_B115200;
+	    break;
+	case B57600:
+	    baud = ASPP_IOCTL_B57600;
+	    break;
+	case B38400:
+	    baud = ASPP_IOCTL_B38400;
+	    break;
+	case B19200:
+	    baud = ASPP_IOCTL_B19200;
+	    break;
+	case B9600:
+	    baud = ASPP_IOCTL_B9600;
+	    break;
+	case B4800:
+	    baud = ASPP_IOCTL_B4800;
+	    break;
+	case B2400:
+	    baud = ASPP_IOCTL_B2400;
+	    break;
+	case B1200:
+	    baud = ASPP_IOCTL_B1200;
+	    break;
+	case B600:
+	    baud = ASPP_IOCTL_B600;
+	    break;
+	case B300:
+	    baud = ASPP_IOCTL_B300;
+	    break;
+	case B150:
+	    baud = ASPP_IOCTL_B150;
+	    break;
+	case B134:
+	    baud = ASPP_IOCTL_B134;
+	    break;
+	case B110:
+	    baud = ASPP_IOCTL_B110;
+	    break;
+	case B75:
+	    baud = ASPP_IOCTL_B75;
+	    break;
+	case B50:
+	    baud = ASPP_IOCTL_B50;
+	    break;
+	default:
+	    baud = 0xff;
+	    break;
+	}
+    }
+
+    cmd[0] = ASPP_CMD_PORT_INIT;
+    cmd[1] = 8;
+    cmd[2] = baud;
+    cmd[3] = mode & 0xff;
+    cmd[4] = 0; // modem_control & UART_MCR_DTR
+    cmd[5] = 0; // modem_control & UART_MCR_RTS
+#if defined(CRTSCTS)
+    cmd[6] = pCEServing->crtscts == FLAGTRUE;
+    cmd[7] = cmd[6];
+#else
+    cmd[6] = cmd[7] = 0;
+#endif
+    cmd[8] = pCEServing->ixon == FLAGTRUE;
+    cmd[9] = pCEServing->ixoff == FLAGTRUE;
+
+    if ((nr =
+	 FileWrite(pCEServing->cocmd, FLAGFALSE, cmd, sizeof(cmd))) < 0) {
+	    Error("[%s] write failure: unexpected EOF", pCEServing->server);
+	    ConsoleError(pCEServing);
+	    return;
+    }
+    CONDDEBUG((1, "DoRealCOMPortInit(): write %d bytes to fd %d", nr,
+	cocmd));
+}
+
+void
+#if PROTOTYPES
+DoRealCOMCmdParse(CONSENT *pCEServing)
+#else
+DoRealCOMCmdParse(pCEServing)
+    CONSENT *pCEServing;
+#endif
+{
+    unsigned char buf[BUFSIZ], cmd[BUFSIZ];
+    int nr, i;
+    int len;
+
+    int cocmd = FileFDNum(pCEServing->cocmd);
+
+    if (!pCEServing->fup) {
+	FD_CLR(cocmd, &rinit);
+	FD_CLR(cocmd, &winit);
+	return;
+    }
+
+    if ((nr =
+	 FileRead(pCEServing->cocmd, buf, sizeof(buf))) < 0) {
+	Error("[%s] read failure: unexpected EOF", pCEServing->server);
+	ConsoleError(pCEServing);
+	return;
+    }
+    CONDDEBUG((1, "DoRealCOMCmdParse(): read %d bytes from fd %d", nr,
+	       cocmd));
+
+    i = 0;
+    len = nr;
+    while (len > 0) {
+	switch (buf[i]) {
+	case ASPP_CMD_POLLING:
+	    if (len < 3) {
+		len = 0;
+		continue;
+	    }
+	    cmd[0] = ASPP_CMD_ALIVE;
+	    cmd[1] = 1;
+	    cmd[2] = buf[i+2];
+	    if ((nr =
+		 FileWrite(pCEServing->cocmd, FLAGFALSE, cmd, 3)) < 0) {
+		Error("[%s] write failure: unexpected EOF", pCEServing->server);
+		ConsoleError(pCEServing);
+		len = 0;
+		continue;
+	    }
+	    CONDDEBUG((1, "DoRealCOMCmdParse(): write %d bytes to fd %d", nr,
+		   cocmd));
+	    break;
+
+	case ASPP_CMD_NOTIFY :
+	case ASPP_CMD_WAIT_OQUEUE :
+	case ASPP_CMD_OQUEUE :
+	case ASPP_CMD_IQUEUE :
+	    nr = 4;
+	    break;
+
+	case ASPP_CMD_LSTATUS :
+	case ASPP_CMD_PORT_INIT:
+	    nr = 5;
+	    break;
+	case ASPP_CMD_FLOWCTRL:
+	case ASPP_CMD_IOCTL:
+	case ASPP_CMD_SETBAUD:
+	case ASPP_CMD_LINECTRL:
+	case ASPP_CMD_START_BREAK:
+	case ASPP_CMD_STOP_BREAK:
+	case ASPP_CMD_START_NOTIFY:
+	case ASPP_CMD_STOP_NOTIFY:
+	case ASPP_CMD_FLUSH:
+	case ASPP_CMD_HOST:
+	case ASPP_CMD_TX_FIFO:
+	case ASPP_CMD_XONXOFF:
+	case ASPP_CMD_SETXON:
+	case ASPP_CMD_SETXOFF:
+	    nr = 3;
+	    break;
+	default :
+	    nr = len;
+	    break;
+	}
+	i += nr;
+	len -= nr;
+    }
+}
+
+void
+#if PROTOTYPES
 DoConsoleRead(CONSENT *pCEServing)
 #else
 DoConsoleRead(pCEServing)
@@ -4217,6 +4450,16 @@ FlushConsole(pCEServing)
 		    break;
 		}
 
+#if 0
+		if (pCEServing->cocmd != NULL &&
+		    !FileBufEmpty(pCEServing->cocmd)) {
+		    CONDDEBUG((1,
+			       "Kiddie(): heavy IAC (wait for flush) for [%s]",
+			       pCEServing->server));
+		    break;
+		}
+#endif
+
 		/* Do the operation */
 		if (next >= '0' && next <= '9') {
 		    int delay = BREAKDELAYDEFAULT;
@@ -4682,8 +4925,10 @@ Kiddie(pGE, sfd)
 			socklen_t slen;
 			int flags = 0;
 			int cofile = FileFDNum(pCEServing->cofile);
+			int cocmd  = FileFDNum(pCEServing->cocmd);
 			slen = sizeof(flags);
 
+			if (pCEServing->type == REALCOM) {
 #if HAVE_OPENSSL
 			if (pCEServing->secured == FLAGTRUE) {
 			    int r;
@@ -4703,6 +4948,8 @@ Kiddie(pGE, sfd)
 			    }
 			}
 #endif
+			    DoRealCOMPortInit(pCEServing);
+			}
 
 			/* So, getsockopt seems to return -1 if there is
 			 * something interesting in SO_ERROR under
@@ -4745,6 +4992,10 @@ Kiddie(pGE, sfd)
 			 */
 			FD_SET(cofile, &rinit);
 			FD_CLR(cofile, &winit);
+			if (cocmd != -1) {
+			    FD_SET(cocmd, &rinit);
+			    FD_CLR(cocmd, &winit);
+			}
 			if (pCEServing->idletimeout != (time_t)0 &&
 			    (timers[T_CIDLE] == (time_t)0 ||
 			     timers[T_CIDLE] >
@@ -4764,6 +5015,8 @@ Kiddie(pGE, sfd)
 		case ISNORMAL:
 		    if (FileCanRead(pCEServing->cofile, &rmask, &wmask))
 			DoConsoleRead(pCEServing);
+		    if (FileCanRead(pCEServing->cocmd, &rmask, &wmask))
+			DoRealCOMCmdParse(pCEServing);
 		    if (FileCanRead(pCEServing->initfile, &rmask, &wmask))
 			DoCommandRead(pCEServing);
 		    /* fall through to ISFLUSHING for buffered data */
diff --git b/conserver/main.c a/conserver/main.c
index 58a3134..fda2bf6 100644
--- b/conserver/main.c
+++ a/conserver/main.c
@@ -953,6 +953,15 @@ DumpDataStructures()
 			       "DumpDataStructures():  host=%s",
 			       EMPTYSTR(pCE->host)));
 		    break;
+		case REALCOM:
+		    CONDDEBUG((1,
+			       "DumpDataStructures():  server=%s, type=REALCOM",
+			       EMPTYSTR(pCE->server)));
+		    CONDDEBUG((1,
+			       "DumpDataStructures():  host=%s, netport=%hu, port=%hu:%hu",
+			       EMPTYSTR(pCE->host),
+			       pCE->netport, pCE->port, pCE->port+16));
+		    break;
 		case HOST:
 		    CONDDEBUG((1,
 			       "DumpDataStructures():  server=%s, type=HOST",
diff --git b/conserver/readcfg.c a/conserver/readcfg.c
index ea6ab1b..fbde4fd 100644
--- b/conserver/readcfg.c
+++ a/conserver/readcfg.c
@@ -2539,6 +2539,8 @@ ProcessType(c, id)
     }
     if (strcasecmp("device", id) == 0)
 	t = DEVICE;
+    else if (strcasecmp("realcom", id) == 0)
+	t = REALCOM;
     else if (strcasecmp("ipmi", id) == 0)
 	t = IPMI;
     else if (strcasecmp("exec", id) == 0)
@@ -2707,6 +2709,20 @@ ConsoleEnd()
 		invalid = 1;
 	    }
 	    break;
+	case REALCOM:
+	    if (parserConsoleTemp->host == (char *)0) {
+		if (isMaster)
+		    Error("[%s] console missing 'host' attribute [%s:%d]",
+			  parserConsoleTemp->server, file, line);
+		invalid = 1;
+	    }
+	    if (parserConsoleTemp->port == 0) {
+		if (isMaster)
+		    Error("[%s] console missing 'port' attribute [%s:%d]",
+			  parserConsoleTemp->server, file, line);
+		invalid = 1;
+	    }
+	    break;
 	case HOST:
 	    if (parserConsoleTemp->host == (char *)0) {
 		if (isMaster)
@@ -3090,6 +3106,17 @@ ConsoleAdd(c)
 		FD_SET(cofile, &winit);
 	}
 
+#if 0
+	if (pCEmatch->cocmd != (CONSFILE *)0) {
+	    int cofile = FileFDNum(pCEmatch->cocmd);
+	    FD_SET(cofile, &rinit);
+	    if (maxfd < cofile + 1)
+		maxfd = cofile + 1;
+	    if (!FileBufEmpty(pCEmatch->cocmd))
+		FD_SET(cofile, &winit);
+	}
+#endif
+
 	if (pCEmatch->initfile != (CONSFILE *)0) {
 	    int initfile = FileFDNum(pCEmatch->initfile);
 	    FD_SET(initfile, &rinit);
@@ -3218,6 +3245,56 @@ ConsoleAdd(c)
 		}
 #endif
 		break;
+	    case REALCOM:
+		if (pCEmatch->host != (char *)0 && c->host != (char *)0) {
+		    if (strcasecmp(pCEmatch->host, c->host) != 0) {
+			SwapStr(&pCEmatch->host, &c->host);
+			closeMatch = 0;
+		    }
+		} else if (pCEmatch->host != (char *)0 ||
+			   c->host != (char *)0) {
+		    SwapStr(&pCEmatch->host, &c->host);
+		    closeMatch = 0;
+		}
+		if (pCEmatch->netport != c->netport) {
+		    pCEmatch->netport = c->netport;
+		    closeMatch = 0;
+		}
+		if (pCEmatch->secured != c->secured) {
+		    pCEmatch->secured = c->secured;
+		    closeMatch = 0;
+		}
+		if (pCEmatch->baud != c->baud) {
+		    pCEmatch->baud = c->baud;
+		    closeMatch = 0;
+		}
+		if (pCEmatch->parity != c->parity) {
+		    pCEmatch->parity = c->parity;
+		    closeMatch = 0;
+		}
+		if (pCEmatch->cstopb != c->cstopb) {
+		    pCEmatch->cstopb = c->cstopb;
+		    closeMatch = 0;
+		}
+		if (pCEmatch->ixany != c->ixany) {
+		    pCEmatch->ixany = c->ixany;
+		    closeMatch = 0;
+		}
+		if (pCEmatch->ixon != c->ixon) {
+		    pCEmatch->ixon = c->ixon;
+		    closeMatch = 0;
+		}
+		if (pCEmatch->ixoff != c->ixoff) {
+		    pCEmatch->ixoff = c->ixoff;
+		    closeMatch = 0;
+		}
+#if defined(CRTSCTS)
+		if (pCEmatch->crtscts != c->crtscts) {
+		    pCEmatch->crtscts = c->crtscts;
+		    closeMatch = 0;
+		}
+#endif
+		break;
 	    case IPMI:
 		if (pCEmatch->host != (char *)0 && c->host != (char *)0) {
 		    if (strcasecmp(pCEmatch->host, c->host) != 0) {
@@ -3424,6 +3501,11 @@ ConsoleDestroy()
 	 * user can shoot himself in the foot with a bad config
 	 * file, but it won't hurt too much.
 	 */
+	if (c->type == REALCOM)
+	    c->netport = c->portbase +
+			((c->port-1) / c->portinc) * c->portinc * 2 +
+			((c->port-1) % c->portinc);
+	else
 	c->netport = c->portbase + c->portinc * c->port;
 
 	/* prepare for substitutions */
@@ -3561,6 +3643,7 @@ ConsoleDestroy()
 				 (c->exec !=
 				  (char *)0 ? c->exec : "/bin/sh")), s);
 		    break;
+		case REALCOM:
 		case HOST:
 		    BuildString(BuildTmpStringPrint
 				("!:%s,%hu", c->host, c->netport), s);
diff --git b/conserver/group.h a/conserver/group.h
index e23d8c3..bc99567 100644
--- b/conserver/group.h
+++ a/conserver/group.h
@@ -50,6 +50,70 @@
 #define AUTH_NOUSER	1	/* no user                              */
 #define AUTH_INVALID	2	/* invalid password                     */
 
+/*
+ * Moxa tty definition
+ */
+#define	ASPP_CMD_NOTIFY		0x26
+#define	ASPP_CMD_POLLING	0x27
+#define	ASPP_CMD_ALIVE		0x28
+
+#define	ASPP_CMD_IOCTL		16
+#define	ASPP_CMD_FLOWCTRL	17
+#define	ASPP_CMD_LSTATUS	19
+#define	ASPP_CMD_LINECTRL	18
+#define	ASPP_CMD_FLUSH		20
+#define	ASPP_CMD_OQUEUE		22
+#define	ASPP_CMD_SETBAUD	23
+#define	ASPP_CMD_START_BREAK	33
+#define	ASPP_CMD_STOP_BREAK	34
+#define	ASPP_CMD_START_NOTIFY	36
+#define	ASPP_CMD_STOP_NOTIFY	37
+#define	ASPP_CMD_HOST		43
+#define	ASPP_CMD_PORT_INIT	44
+#define	ASPP_CMD_WAIT_OQUEUE 	47
+
+#define	ASPP_CMD_IQUEUE		21
+#define	ASPP_CMD_XONXOFF	24
+#define	ASPP_CMD_PORT_RESET	32
+#define	ASPP_CMD_RESENT_TIME	46
+#define	ASPP_CMD_TX_FIFO	48
+#define ASPP_CMD_SETXON     51
+#define ASPP_CMD_SETXOFF    52
+
+#define	ASPP_IOCTL_B300			0
+#define	ASPP_IOCTL_B600			1
+#define	ASPP_IOCTL_B1200		2
+#define	ASPP_IOCTL_B2400		3
+#define	ASPP_IOCTL_B4800		4
+#define	ASPP_IOCTL_B7200		5
+#define	ASPP_IOCTL_B9600		6
+#define	ASPP_IOCTL_B19200		7
+#define	ASPP_IOCTL_B38400		8
+#define	ASPP_IOCTL_B57600		9
+#define	ASPP_IOCTL_B115200		10
+#define	ASPP_IOCTL_B230400		11
+#define	ASPP_IOCTL_B460800		12
+#define	ASPP_IOCTL_B921600		13
+#define	ASPP_IOCTL_B150			14
+#define	ASPP_IOCTL_B134			15
+#define	ASPP_IOCTL_B110			16
+#define	ASPP_IOCTL_B75			17
+#define	ASPP_IOCTL_B50			18
+
+#define	ASPP_IOCTL_BITS8		3
+#define	ASPP_IOCTL_BITS7		2
+#define	ASPP_IOCTL_BITS6		1
+#define	ASPP_IOCTL_BITS5		0
+
+#define	ASPP_IOCTL_STOP1		0
+#define	ASPP_IOCTL_STOP2		4
+
+#define	ASPP_IOCTL_EVEN			8
+#define	ASPP_IOCTL_ODD			16
+#define	ASPP_IOCTL_MARK			24
+#define	ASPP_IOCTL_SPACE		32
+#define	ASPP_IOCTL_NONE			0
+
 typedef struct grpent {		/* group info                           */
     unsigned int id;		/* uniqueue group id                    */
     unsigned short port;	/* port group listens on                */