// $Id: Rw11VirtTermTcp.cpp 521 2013-05-20 22:16:45Z mueller $
|
// $Id: Rw11VirtTermTcp.cpp 521 2013-05-20 22:16:45Z mueller $
|
//
|
//
|
// Copyright 2013- by Walter F.J. Mueller <W.F.J.Mueller@gsi.de>
|
// Copyright 2013- by Walter F.J. Mueller <W.F.J.Mueller@gsi.de>
|
//
|
//
|
// This program is free software; you may redistribute and/or modify it under
|
// This program is free software; you may redistribute and/or modify it under
|
// the terms of the GNU General Public License as published by the Free
|
// the terms of the GNU General Public License as published by the Free
|
// Software Foundation, either version 2, or at your option any later version.
|
// Software Foundation, either version 2, or at your option any later version.
|
//
|
//
|
// This program is distributed in the hope that it will be useful, but
|
// This program is distributed in the hope that it will be useful, but
|
// WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
|
// WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
|
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// for complete details.
|
// for complete details.
|
//
|
//
|
// Revision History:
|
// Revision History:
|
// Date Rev Version Comment
|
// Date Rev Version Comment
|
// 2013-05-17 512 1.0.3 use Rtools::String2Long
|
// 2013-05-17 512 1.0.3 use Rtools::String2Long
|
// 2013-05-05 516 1.0.2 fix mistakes in emsg generation with errno
|
// 2013-05-05 516 1.0.2 fix mistakes in emsg generation with errno
|
// 2013-04-20 508 1.0.1 add fSndPreConQue handling
|
// 2013-04-20 508 1.0.1 add fSndPreConQue handling
|
// 2013-03-06 495 1.0 Initial version
|
// 2013-03-06 495 1.0 Initial version
|
// 2013-02-13 488 0.1 First draft
|
// 2013-02-13 488 0.1 First draft
|
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
|
|
/*!
|
/*!
|
\file
|
\file
|
\version $Id: Rw11VirtTermTcp.cpp 521 2013-05-20 22:16:45Z mueller $
|
\version $Id: Rw11VirtTermTcp.cpp 521 2013-05-20 22:16:45Z mueller $
|
\brief Implemenation of Rw11VirtTermTcp.
|
\brief Implemenation of Rw11VirtTermTcp.
|
*/
|
*/
|
|
|
#include <sys/types.h>
|
#include <sys/types.h>
|
#include <sys/socket.h>
|
#include <sys/socket.h>
|
#include <netdb.h>
|
#include <netdb.h>
|
#include <string.h>
|
#include <string.h>
|
|
|
#include <sstream>
|
#include <sstream>
|
|
|
#include "librtools/RosFill.hpp"
|
#include "librtools/RosFill.hpp"
|
#include "librtools/RlogMsg.hpp"
|
#include "librtools/RlogMsg.hpp"
|
|
|
#include "Rw11VirtTermTcp.hpp"
|
#include "Rw11VirtTermTcp.hpp"
|
|
|
using namespace std;
|
using namespace std;
|
|
|
/*!
|
/*!
|
\class Retro::Rw11VirtTermTcp
|
\class Retro::Rw11VirtTermTcp
|
\brief FIXME_docs
|
\brief FIXME_docs
|
*/
|
*/
|
|
|
// all method definitions in namespace Retro
|
// all method definitions in namespace Retro
|
namespace Retro {
|
namespace Retro {
|
|
|
//------------------------------------------+-----------------------------------
|
//------------------------------------------+-----------------------------------
|
// constants definitions
|
// constants definitions
|
|
|
const uint8_t Rw11VirtTermTcp::kCode_NULL;
|
const uint8_t Rw11VirtTermTcp::kCode_NULL;
|
const uint8_t Rw11VirtTermTcp::kCode_LF;
|
const uint8_t Rw11VirtTermTcp::kCode_LF;
|
const uint8_t Rw11VirtTermTcp::kCode_CR;
|
const uint8_t Rw11VirtTermTcp::kCode_CR;
|
const uint8_t Rw11VirtTermTcp::kCode_ESC;
|
const uint8_t Rw11VirtTermTcp::kCode_ESC;
|
const uint8_t Rw11VirtTermTcp::kCode_SE;
|
const uint8_t Rw11VirtTermTcp::kCode_SE;
|
const uint8_t Rw11VirtTermTcp::kCode_NOP;
|
const uint8_t Rw11VirtTermTcp::kCode_NOP;
|
const uint8_t Rw11VirtTermTcp::kCode_IP;
|
const uint8_t Rw11VirtTermTcp::kCode_IP;
|
const uint8_t Rw11VirtTermTcp::kCode_GA;
|
const uint8_t Rw11VirtTermTcp::kCode_GA;
|
const uint8_t Rw11VirtTermTcp::kCode_SB;
|
const uint8_t Rw11VirtTermTcp::kCode_SB;
|
const uint8_t Rw11VirtTermTcp::kCode_WILL;
|
const uint8_t Rw11VirtTermTcp::kCode_WILL;
|
const uint8_t Rw11VirtTermTcp::kCode_WONT;
|
const uint8_t Rw11VirtTermTcp::kCode_WONT;
|
const uint8_t Rw11VirtTermTcp::kCode_DO;
|
const uint8_t Rw11VirtTermTcp::kCode_DO;
|
const uint8_t Rw11VirtTermTcp::kCode_DONT;
|
const uint8_t Rw11VirtTermTcp::kCode_DONT;
|
const uint8_t Rw11VirtTermTcp::kCode_IAC;
|
const uint8_t Rw11VirtTermTcp::kCode_IAC;
|
|
|
const uint8_t Rw11VirtTermTcp::kOpt_BIN;
|
const uint8_t Rw11VirtTermTcp::kOpt_BIN;
|
const uint8_t Rw11VirtTermTcp::kOpt_ECHO;
|
const uint8_t Rw11VirtTermTcp::kOpt_ECHO;
|
const uint8_t Rw11VirtTermTcp::kOpt_SGA;
|
const uint8_t Rw11VirtTermTcp::kOpt_SGA;
|
const uint8_t Rw11VirtTermTcp::kOpt_TTYP;
|
const uint8_t Rw11VirtTermTcp::kOpt_TTYP;
|
const uint8_t Rw11VirtTermTcp::kOpt_LINE;
|
const uint8_t Rw11VirtTermTcp::kOpt_LINE;
|
|
|
const size_t Rw11VirtTermTcp::kPreConQue_limit;
|
const size_t Rw11VirtTermTcp::kPreConQue_limit;
|
|
|
//------------------------------------------+-----------------------------------
|
//------------------------------------------+-----------------------------------
|
//! Default constructor
|
//! Default constructor
|
|
|
Rw11VirtTermTcp::Rw11VirtTermTcp(Rw11Unit* punit)
|
Rw11VirtTermTcp::Rw11VirtTermTcp(Rw11Unit* punit)
|
: Rw11VirtTerm(punit),
|
: Rw11VirtTerm(punit),
|
fFdListen(-1),
|
fFdListen(-1),
|
fFd(-1),
|
fFd(-1),
|
fState(ts_Closed),
|
fState(ts_Closed),
|
fTcpTrace(false),
|
fTcpTrace(false),
|
fSndPreConQue()
|
fSndPreConQue()
|
{
|
{
|
fStats.Define(kStatNVTPreConSave , "NVTPreConSave" ,
|
fStats.Define(kStatNVTPreConSave , "NVTPreConSave" ,
|
"VT snd bytes saved prior connect");
|
"VT snd bytes saved prior connect");
|
fStats.Define(kStatNVTPreConDrop , "NVTPreConDrop" ,
|
fStats.Define(kStatNVTPreConDrop , "NVTPreConDrop" ,
|
"VT snd bytes dropped prior connect");
|
"VT snd bytes dropped prior connect");
|
fStats.Define(kStatNVTListenPoll , "NVTListenPoll" ,
|
fStats.Define(kStatNVTListenPoll , "NVTListenPoll" ,
|
"VT ListenPollHandler() calls");
|
"VT ListenPollHandler() calls");
|
fStats.Define(kStatNVTAccept, "NVTAccept", "VT socket accepts");
|
fStats.Define(kStatNVTAccept, "NVTAccept", "VT socket accepts");
|
fStats.Define(kStatNVTRcvRaw, "NVTRcvRaw", "VT raw bytes received");
|
fStats.Define(kStatNVTRcvRaw, "NVTRcvRaw", "VT raw bytes received");
|
fStats.Define(kStatNVTSndRaw, "NVTSndRaw", "VT raw bytes send");
|
fStats.Define(kStatNVTSndRaw, "NVTSndRaw", "VT raw bytes send");
|
}
|
}
|
|
|
//------------------------------------------+-----------------------------------
|
//------------------------------------------+-----------------------------------
|
//! Destructor
|
//! Destructor
|
|
|
Rw11VirtTermTcp::~Rw11VirtTermTcp()
|
Rw11VirtTermTcp::~Rw11VirtTermTcp()
|
{
|
{
|
if (fFdListen > 2) {
|
if (fFdListen > 2) {
|
Server().RemovePollHandler(fFdListen);
|
Server().RemovePollHandler(fFdListen);
|
close(fFdListen);
|
close(fFdListen);
|
}
|
}
|
if (Connected()) {
|
if (Connected()) {
|
Server().RemovePollHandler(fFd);
|
Server().RemovePollHandler(fFd);
|
close(fFd);
|
close(fFd);
|
}
|
}
|
}
|
}
|
|
|
//------------------------------------------+-----------------------------------
|
//------------------------------------------+-----------------------------------
|
//! FIXME_docs
|
//! FIXME_docs
|
|
|
bool Rw11VirtTermTcp::Open(const std::string& url, RerrMsg& emsg)
|
bool Rw11VirtTermTcp::Open(const std::string& url, RerrMsg& emsg)
|
{
|
{
|
if (!fUrl.Set(url, "|port=|trace|", emsg)) return false;
|
if (!fUrl.Set(url, "|port=|trace|", emsg)) return false;
|
if (!(fUrl.FindOpt("port"))) {
|
if (!(fUrl.FindOpt("port"))) {
|
emsg.Init("Rw11VirtTermTcp::Open", "port= option not specified");
|
emsg.Init("Rw11VirtTermTcp::Open", "port= option not specified");
|
return false;
|
return false;
|
}
|
}
|
|
|
fTcpTrace = fUrl.FindOpt("trace");
|
fTcpTrace = fUrl.FindOpt("trace");
|
|
|
string port;
|
string port;
|
fUrl.FindOpt("port",port);
|
fUrl.FindOpt("port",port);
|
unsigned long portno;
|
unsigned long portno;
|
if (!Rtools::String2Long(port, portno, emsg)) return false;
|
if (!Rtools::String2Long(port, portno, emsg)) return false;
|
|
|
protoent* pe = getprotobyname("tcp");
|
protoent* pe = getprotobyname("tcp");
|
if (pe == 0) {
|
if (pe == 0) {
|
emsg.Init("Rw11VirtTermTcp::Open","getprotobyname(\"tcp\") failed");
|
emsg.Init("Rw11VirtTermTcp::Open","getprotobyname(\"tcp\") failed");
|
return false;
|
return false;
|
}
|
}
|
|
|
int fd = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, pe->p_proto);
|
int fd = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, pe->p_proto);
|
if (fd < 0) {
|
if (fd < 0) {
|
emsg.InitErrno("Rw11VirtTermTcp::Open","socket() failed: ", errno);
|
emsg.InitErrno("Rw11VirtTermTcp::Open","socket() failed: ", errno);
|
return false;
|
return false;
|
}
|
}
|
|
|
int on = 1;
|
int on = 1;
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
|
emsg.InitErrno("Rw11VirtTermTcp::Open","setsockop() failed: ", errno);
|
emsg.InitErrno("Rw11VirtTermTcp::Open","setsockop() failed: ", errno);
|
close(fd);
|
close(fd);
|
return false;
|
return false;
|
}
|
}
|
|
|
sockaddr_in sa;
|
sockaddr_in sa;
|
memset(&sa, 0, sizeof(sa));
|
memset(&sa, 0, sizeof(sa));
|
sa.sin_family = AF_INET;
|
sa.sin_family = AF_INET;
|
sa.sin_port = htons((unsigned short) portno);
|
sa.sin_port = htons((unsigned short) portno);
|
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
// Note: ::bind needed below to avoid collision with std::bind...
|
// Note: ::bind needed below to avoid collision with std::bind...
|
if (::bind(fd, (sockaddr*) &sa, sizeof(sa)) < 0) {
|
if (::bind(fd, (sockaddr*) &sa, sizeof(sa)) < 0) {
|
emsg.InitErrno("Rw11VirtTermTcp::Open","bind() failed: ", errno);
|
emsg.InitErrno("Rw11VirtTermTcp::Open","bind() failed: ", errno);
|
close(fd);
|
close(fd);
|
return false;
|
return false;
|
}
|
}
|
|
|
if (listen(fd, 1) <0) {
|
if (listen(fd, 1) <0) {
|
emsg.InitErrno("Rw11VirtTermTcp::Open","listen() failed: ", errno);
|
emsg.InitErrno("Rw11VirtTermTcp::Open","listen() failed: ", errno);
|
close(fd);
|
close(fd);
|
return false;
|
return false;
|
}
|
}
|
|
|
fFdListen = fd;
|
fFdListen = fd;
|
fChannelId = port;
|
fChannelId = port;
|
fState = ts_Listen;
|
fState = ts_Listen;
|
|
|
if (fTcpTrace) {
|
if (fTcpTrace) {
|
RlogMsg lmsg(LogFile(),'I');
|
RlogMsg lmsg(LogFile(),'I');
|
lmsg << "TermTcp: listen on " << fChannelId << " for " << Unit().Name();
|
lmsg << "TermTcp: listen on " << fChannelId << " for " << Unit().Name();
|
}
|
}
|
|
|
Server().AddPollHandler(boost::bind(&Rw11VirtTermTcp::ListenPollHandler,
|
Server().AddPollHandler(boost::bind(&Rw11VirtTermTcp::ListenPollHandler,
|
this, _1),
|
this, _1),
|
fFdListen, POLLIN);
|
fFdListen, POLLIN);
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
//------------------------------------------+-----------------------------------
|
//------------------------------------------+-----------------------------------
|
//! FIXME_docs
|
//! FIXME_docs
|
|
|
bool Rw11VirtTermTcp::Snd(const uint8_t* data, size_t count, RerrMsg& emsg)
|
bool Rw11VirtTermTcp::Snd(const uint8_t* data, size_t count, RerrMsg& emsg)
|
{
|
{
|
fStats.Inc(kStatNVTSnd);
|
fStats.Inc(kStatNVTSnd);
|
const uint8_t* pdata = data;
|
const uint8_t* pdata = data;
|
const uint8_t* pdataend = data+count;
|
const uint8_t* pdataend = data+count;
|
if (count == 0) return true; // quit if nothing to do
|
if (count == 0) return true; // quit if nothing to do
|
|
|
if (!Connected()) { // if not connected keep last chars
|
if (!Connected()) { // if not connected keep last chars
|
for (size_t i=0; i<count; i++) fSndPreConQue.push_back(data[i]);
|
for (size_t i=0; i<count; i++) fSndPreConQue.push_back(data[i]);
|
fStats.Inc(kStatNVTPreConSave, double(count));
|
fStats.Inc(kStatNVTPreConSave, double(count));
|
while (fSndPreConQue.size() > kPreConQue_limit) {
|
while (fSndPreConQue.size() > kPreConQue_limit) {
|
fSndPreConQue.pop_front();
|
fSndPreConQue.pop_front();
|
fStats.Inc(kStatNVTPreConDrop);
|
fStats.Inc(kStatNVTPreConDrop);
|
}
|
}
|
return true;
|
return true;
|
}
|
}
|
|
|
uint8_t obuf[1024];
|
uint8_t obuf[1024];
|
while (pdata < pdataend) {
|
while (pdata < pdataend) {
|
uint8_t* pobuf = obuf;
|
uint8_t* pobuf = obuf;
|
uint8_t* pobufend = obuf+1024;
|
uint8_t* pobufend = obuf+1024;
|
while (pdata < pdataend && pobuf < pobufend-1) {
|
while (pdata < pdataend && pobuf < pobufend-1) {
|
if (*pdata == kCode_IAC) *pobuf++ = kCode_IAC;
|
if (*pdata == kCode_IAC) *pobuf++ = kCode_IAC;
|
*pobuf++ = *pdata++;
|
*pobuf++ = *pdata++;
|
}
|
}
|
|
|
int irc = write(fFd, obuf, pobuf-obuf);
|
int irc = write(fFd, obuf, pobuf-obuf);
|
if (irc < 0) {
|
if (irc < 0) {
|
RlogMsg lmsg(LogFile(),'E');
|
RlogMsg lmsg(LogFile(),'E');
|
RerrMsg emsg("Rw11VirtTermTcp::Snd",
|
RerrMsg emsg("Rw11VirtTermTcp::Snd",
|
string("write() for port ") + fChannelId + " failed: ",
|
string("write() for port ") + fChannelId + " failed: ",
|
errno);
|
errno);
|
lmsg << emsg;
|
lmsg << emsg;
|
} else {
|
} else {
|
fStats.Inc(kStatNVTSndRaw, double(irc));
|
fStats.Inc(kStatNVTSndRaw, double(irc));
|
}
|
}
|
}
|
}
|
|
|
fStats.Inc(kStatNVTSndByt, double(count));
|
fStats.Inc(kStatNVTSndByt, double(count));
|
return true;
|
return true;
|
}
|
}
|
|
|
//------------------------------------------+-----------------------------------
|
//------------------------------------------+-----------------------------------
|
//! FIXME_docs
|
//! FIXME_docs
|
|
|
void Rw11VirtTermTcp::Dump(std::ostream& os, int ind, const char* text) const
|
void Rw11VirtTermTcp::Dump(std::ostream& os, int ind, const char* text) const
|
{
|
{
|
RosFill bl(ind);
|
RosFill bl(ind);
|
os << bl << (text?text:"--") << "Rw11VirtTermTcp @ " << this << endl;
|
os << bl << (text?text:"--") << "Rw11VirtTermTcp @ " << this << endl;
|
|
|
os << bl << " fFdListen: " << fFdListen << endl;
|
os << bl << " fFdListen: " << fFdListen << endl;
|
os << bl << " fFd: " << fFd << endl;
|
os << bl << " fFd: " << fFd << endl;
|
const char* t_state = "";
|
const char* t_state = "";
|
switch (fState) {
|
switch (fState) {
|
case ts_Closed: t_state = "ts_Closed"; break;
|
case ts_Closed: t_state = "ts_Closed"; break;
|
case ts_Listen: t_state = "ts_Listen"; break;
|
case ts_Listen: t_state = "ts_Listen"; break;
|
case ts_Stream: t_state = "ts_Stream"; break;
|
case ts_Stream: t_state = "ts_Stream"; break;
|
case ts_Iac: t_state = "ts_Iac"; break;
|
case ts_Iac: t_state = "ts_Iac"; break;
|
case ts_Cmd: t_state = "ts_Cmd"; break;
|
case ts_Cmd: t_state = "ts_Cmd"; break;
|
case ts_Subneg: t_state = "ts_Subneg"; break;
|
case ts_Subneg: t_state = "ts_Subneg"; break;
|
case ts_Subiac: t_state = "ts_Subiac"; break;
|
case ts_Subiac: t_state = "ts_Subiac"; break;
|
default: t_state = "???";
|
default: t_state = "???";
|
}
|
}
|
os << bl << " fState: " << t_state << endl;
|
os << bl << " fState: " << t_state << endl;
|
os << bl << " fTcpTrace: " << fTcpTrace << endl;
|
os << bl << " fTcpTrace: " << fTcpTrace << endl;
|
os << bl << " fSndPreConQue.size" << fSndPreConQue.size() << endl;
|
os << bl << " fSndPreConQue.size" << fSndPreConQue.size() << endl;
|
Rw11VirtTerm::Dump(os, ind, " ^");
|
Rw11VirtTerm::Dump(os, ind, " ^");
|
return;
|
return;
|
}
|
}
|
|
|
//------------------------------------------+-----------------------------------
|
//------------------------------------------+-----------------------------------
|
//! FIXME_docs
|
//! FIXME_docs
|
|
|
int Rw11VirtTermTcp::ListenPollHandler(const pollfd& pfd)
|
int Rw11VirtTermTcp::ListenPollHandler(const pollfd& pfd)
|
{
|
{
|
// bail-out and cancel handler if poll returns an error event
|
// bail-out and cancel handler if poll returns an error event
|
if (pfd.revents & (~pfd.events)) return -1;
|
if (pfd.revents & (~pfd.events)) return -1;
|
|
|
fFd = accept(fFdListen, NULL, 0);
|
fFd = accept(fFdListen, NULL, 0);
|
|
|
if (fFd < 0) {
|
if (fFd < 0) {
|
RlogMsg lmsg(LogFile(),'E');
|
RlogMsg lmsg(LogFile(),'E');
|
RerrMsg emsg("Rw11VirtTermTcp::ListenPollHandler",
|
RerrMsg emsg("Rw11VirtTermTcp::ListenPollHandler",
|
string("accept() for port ") + fChannelId + " failed: ",
|
string("accept() for port ") + fChannelId + " failed: ",
|
errno);
|
errno);
|
lmsg << emsg;
|
lmsg << emsg;
|
// FIXME_code: proper error handling
|
// FIXME_code: proper error handling
|
return 0;
|
return 0;
|
}
|
}
|
|
|
fStats.Inc(kStatNVTAccept);
|
fStats.Inc(kStatNVTAccept);
|
|
|
uint8_t buf_1[3] = {kCode_IAC, kCode_WILL, kOpt_LINE};
|
uint8_t buf_1[3] = {kCode_IAC, kCode_WILL, kOpt_LINE};
|
uint8_t buf_2[3] = {kCode_IAC, kCode_WILL, kOpt_SGA};
|
uint8_t buf_2[3] = {kCode_IAC, kCode_WILL, kOpt_SGA};
|
uint8_t buf_3[3] = {kCode_IAC, kCode_WILL, kOpt_ECHO};
|
uint8_t buf_3[3] = {kCode_IAC, kCode_WILL, kOpt_ECHO};
|
uint8_t buf_4[3] = {kCode_IAC, kCode_WILL, kOpt_BIN};
|
uint8_t buf_4[3] = {kCode_IAC, kCode_WILL, kOpt_BIN};
|
uint8_t buf_5[3] = {kCode_IAC, kCode_DO , kOpt_BIN};
|
uint8_t buf_5[3] = {kCode_IAC, kCode_DO , kOpt_BIN};
|
|
|
int nerr = 0;
|
int nerr = 0;
|
|
|
// send initial negotiation WILLs and DOs
|
// send initial negotiation WILLs and DOs
|
if (write(fFd, buf_1, sizeof(buf_1)) < 0) nerr += 1;
|
if (write(fFd, buf_1, sizeof(buf_1)) < 0) nerr += 1;
|
if (write(fFd, buf_2, sizeof(buf_2)) < 0) nerr += 1;
|
if (write(fFd, buf_2, sizeof(buf_2)) < 0) nerr += 1;
|
if (write(fFd, buf_3, sizeof(buf_3)) < 0) nerr += 1;
|
if (write(fFd, buf_3, sizeof(buf_3)) < 0) nerr += 1;
|
if (write(fFd, buf_4, sizeof(buf_4)) < 0) nerr += 1;
|
if (write(fFd, buf_4, sizeof(buf_4)) < 0) nerr += 1;
|
if (write(fFd, buf_5, sizeof(buf_5)) < 0) nerr += 1;
|
if (write(fFd, buf_5, sizeof(buf_5)) < 0) nerr += 1;
|
|
|
// send connect message
|
// send connect message
|
if (nerr==0) {
|
if (nerr==0) {
|
stringstream msg;
|
stringstream msg;
|
msg << "\r\nconnect on port " << fChannelId
|
msg << "\r\nconnect on port " << fChannelId
|
<< " for " << Unit().Name() << "\r\n\r\n";
|
<< " for " << Unit().Name() << "\r\n\r\n";
|
string str = msg.str();
|
string str = msg.str();
|
if (write(fFd, str.c_str(), str.length()) < 0) nerr += 1;
|
if (write(fFd, str.c_str(), str.length()) < 0) nerr += 1;
|
}
|
}
|
|
|
// send chars buffered while attached but not connected
|
// send chars buffered while attached but not connected
|
if (nerr==0 && fSndPreConQue.size()) {
|
if (nerr==0 && fSndPreConQue.size()) {
|
stringstream msg;
|
stringstream msg;
|
while (!fSndPreConQue.empty()) {
|
while (!fSndPreConQue.empty()) {
|
msg << char(fSndPreConQue.front());
|
msg << char(fSndPreConQue.front());
|
fSndPreConQue.pop_front();
|
fSndPreConQue.pop_front();
|
}
|
}
|
string str = msg.str();
|
string str = msg.str();
|
if (write(fFd, str.c_str(), str.length()) < 0) nerr += 1;
|
if (write(fFd, str.c_str(), str.length()) < 0) nerr += 1;
|
}
|
}
|
|
|
if (nerr) {
|
if (nerr) {
|
close(fFd);
|
close(fFd);
|
fFd = -1;
|
fFd = -1;
|
RlogMsg lmsg(LogFile(),'E');
|
RlogMsg lmsg(LogFile(),'E');
|
RerrMsg emsg("Rw11VirtTermTcp::ListenPollHandler",
|
RerrMsg emsg("Rw11VirtTermTcp::ListenPollHandler",
|
string("initial write()s for port ") + fChannelId +
|
string("initial write()s for port ") + fChannelId +
|
" failed: ", errno);
|
" failed: ", errno);
|
lmsg << emsg;
|
lmsg << emsg;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
if (fTcpTrace) {
|
if (fTcpTrace) {
|
RlogMsg lmsg(LogFile(),'I');
|
RlogMsg lmsg(LogFile(),'I');
|
lmsg << "TermTcp: accept on " << fChannelId << " for " << Unit().Name();
|
lmsg << "TermTcp: accept on " << fChannelId << " for " << Unit().Name();
|
}
|
}
|
|
|
fState = ts_Stream;
|
fState = ts_Stream;
|
|
|
Server().RemovePollHandler(fFdListen);
|
Server().RemovePollHandler(fFdListen);
|
Server().AddPollHandler(boost::bind(&Rw11VirtTermTcp::RcvPollHandler,
|
Server().AddPollHandler(boost::bind(&Rw11VirtTermTcp::RcvPollHandler,
|
this, _1),
|
this, _1),
|
fFd, POLLIN);
|
fFd, POLLIN);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
//------------------------------------------+-----------------------------------
|
//------------------------------------------+-----------------------------------
|
//! FIXME_docs
|
//! FIXME_docs
|
|
|
int Rw11VirtTermTcp::RcvPollHandler(const pollfd& pfd)
|
int Rw11VirtTermTcp::RcvPollHandler(const pollfd& pfd)
|
{
|
{
|
fStats.Inc(kStatNVTRcvPoll);
|
fStats.Inc(kStatNVTRcvPoll);
|
|
|
int irc = -1;
|
int irc = -1;
|
|
|
if (pfd.revents & POLLIN) {
|
if (pfd.revents & POLLIN) {
|
uint8_t ibuf[1024];
|
uint8_t ibuf[1024];
|
uint8_t obuf[1024];
|
uint8_t obuf[1024];
|
uint8_t* pobuf = obuf;
|
uint8_t* pobuf = obuf;
|
|
|
irc = read(fFd, ibuf, 1024);
|
irc = read(fFd, ibuf, 1024);
|
|
|
if (irc < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) return 0;
|
if (irc < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) return 0;
|
|
|
if (irc > 0) {
|
if (irc > 0) {
|
fStats.Inc(kStatNVTRcvRaw, double(irc));
|
fStats.Inc(kStatNVTRcvRaw, double(irc));
|
for (int i=0; i<irc; i++) {
|
for (int i=0; i<irc; i++) {
|
uint8_t byt = ibuf[i];
|
uint8_t byt = ibuf[i];
|
switch (fState) {
|
switch (fState) {
|
case ts_Stream:
|
case ts_Stream:
|
if (byt == kCode_IAC) {
|
if (byt == kCode_IAC) {
|
fState = ts_Iac;
|
fState = ts_Iac;
|
} else {
|
} else {
|
*pobuf++ = byt;
|
*pobuf++ = byt;
|
fStats.Inc(kStatNVTRcvByt, 1.);
|
fStats.Inc(kStatNVTRcvByt, 1.);
|
}
|
}
|
break;
|
break;
|
|
|
case ts_Iac:
|
case ts_Iac:
|
if (byt == kCode_WILL || byt == kCode_WONT ||
|
if (byt == kCode_WILL || byt == kCode_WONT ||
|
byt == kCode_DO || byt == kCode_DONT) {
|
byt == kCode_DO || byt == kCode_DONT) {
|
fState = ts_Cmd;
|
fState = ts_Cmd;
|
} else if (byt == kCode_SB) {
|
} else if (byt == kCode_SB) {
|
fState = ts_Subneg;
|
fState = ts_Subneg;
|
} else {
|
} else {
|
fState = ts_Stream;
|
fState = ts_Stream;
|
}
|
}
|
break;
|
break;
|
|
|
case ts_Cmd:
|
case ts_Cmd:
|
fState = ts_Stream;
|
fState = ts_Stream;
|
break;
|
break;
|
|
|
case ts_Subneg:
|
case ts_Subneg:
|
if (byt == kCode_IAC) {
|
if (byt == kCode_IAC) {
|
fState = ts_Subiac;
|
fState = ts_Subiac;
|
}
|
}
|
break;
|
break;
|
|
|
case ts_Subiac:
|
case ts_Subiac:
|
fState = ts_Stream;
|
fState = ts_Stream;
|
break;
|
break;
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
|
|
}
|
}
|
}
|
}
|
|
|
if (pobuf > obuf) fRcvCb(obuf, pobuf - obuf);
|
if (pobuf > obuf) fRcvCb(obuf, pobuf - obuf);
|
}
|
}
|
|
|
|
|
if (irc <= 0) {
|
if (irc <= 0) {
|
if (irc < 0) {
|
if (irc < 0) {
|
RlogMsg lmsg(LogFile(),'E');
|
RlogMsg lmsg(LogFile(),'E');
|
RerrMsg emsg("Rw11VirtTermTcp::ListenPollHandler",
|
RerrMsg emsg("Rw11VirtTermTcp::ListenPollHandler",
|
string("read() for port ") + fChannelId + " failed: ",
|
string("read() for port ") + fChannelId + " failed: ",
|
errno);
|
errno);
|
lmsg << emsg;
|
lmsg << emsg;
|
}
|
}
|
if (fTcpTrace) {
|
if (fTcpTrace) {
|
RlogMsg lmsg(LogFile(),'I');
|
RlogMsg lmsg(LogFile(),'I');
|
lmsg << "TermTcp: close on " << fChannelId << " for " << Unit().Name();
|
lmsg << "TermTcp: close on " << fChannelId << " for " << Unit().Name();
|
}
|
}
|
close(fFd);
|
close(fFd);
|
fFd = -1;
|
fFd = -1;
|
Server().AddPollHandler(boost::bind(&Rw11VirtTermTcp::ListenPollHandler,
|
Server().AddPollHandler(boost::bind(&Rw11VirtTermTcp::ListenPollHandler,
|
this, _1),
|
this, _1),
|
fFdListen, POLLIN);
|
fFdListen, POLLIN);
|
fState = ts_Listen;
|
fState = ts_Listen;
|
return -1;
|
return -1;
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
} // end namespace Retro
|
} // end namespace Retro
|
|
|