w11 - cpp 0.794
Backend server for Rlink and w11
Loading...
Searching...
No Matches
Rw11VirtTermTcp.cpp
Go to the documentation of this file.
1// $Id: Rw11VirtTermTcp.cpp 1186 2019-07-12 17:49:59Z mueller $
2// SPDX-License-Identifier: GPL-3.0-or-later
3// Copyright 2013-2019 by Walter F.J. Mueller <W.F.J.Mueller@gsi.de>
4//
5// Revision History:
6// Date Rev Version Comment
7// 2019-02-23 1114 1.0.14 use std::bind instead of lambda
8// 2018-12-22 1091 1.0.13 pfd->pfd1 (-Wshadow fix)
9// 2018-12-19 1090 1.0.12 use RosPrintf(bool)
10// 2018-12-18 1089 1.0.11 use c++ style casts
11// 2018-12-15 1082 1.0.10 use lambda instead of boost::bind
12// 2018-11-30 1075 1.0.9 use list-init
13// 2018-11-11 1066 1.0.8 coverity fixup (unchecked return value)
14// 2018-10-27 1059 1.0.7 coverity fixup (uncaught exception in dtor)
15// 2017-04-15 875 1.0.6 Open(): set default scheme
16// 2017-04-07 868 1.0.5 Dump(): add detail arg
17// 2014-08-22 584 1.0.4 use nullptr
18// 2013-05-17 512 1.0.3 use Rtools::String2Long
19// 2013-05-05 516 1.0.2 fix mistakes in emsg generation with errno
20// 2013-04-20 508 1.0.1 add fSndPreConQue handling
21// 2013-03-06 495 1.0 Initial version
22// 2013-02-13 488 0.1 First draft
23// ---------------------------------------------------------------------------
24
29#include <sys/types.h>
30#include <sys/socket.h>
31#include <netdb.h>
32#include <unistd.h>
33
34#include <sstream>
35#include <functional>
36
37#include "librtools/RosFill.hpp"
39#include "librtools/RlogMsg.hpp"
40#include "librtools/Rtools.hpp"
41
42#include "Rw11VirtTermTcp.hpp"
43
44using namespace std;
45using namespace std::placeholders;
46
52// all method definitions in namespace Retro
53namespace Retro {
54
55//------------------------------------------+-----------------------------------
56// constants definitions
57
58const uint8_t Rw11VirtTermTcp::kCode_NULL;
59const uint8_t Rw11VirtTermTcp::kCode_LF;
60const uint8_t Rw11VirtTermTcp::kCode_CR;
61const uint8_t Rw11VirtTermTcp::kCode_ESC;
62const uint8_t Rw11VirtTermTcp::kCode_SE;
63const uint8_t Rw11VirtTermTcp::kCode_NOP;
64const uint8_t Rw11VirtTermTcp::kCode_IP;
65const uint8_t Rw11VirtTermTcp::kCode_GA;
66const uint8_t Rw11VirtTermTcp::kCode_SB;
67const uint8_t Rw11VirtTermTcp::kCode_WILL;
68const uint8_t Rw11VirtTermTcp::kCode_WONT;
69const uint8_t Rw11VirtTermTcp::kCode_DO;
70const uint8_t Rw11VirtTermTcp::kCode_DONT;
71const uint8_t Rw11VirtTermTcp::kCode_IAC;
72
73const uint8_t Rw11VirtTermTcp::kOpt_BIN;
74const uint8_t Rw11VirtTermTcp::kOpt_ECHO;
75const uint8_t Rw11VirtTermTcp::kOpt_SGA;
76const uint8_t Rw11VirtTermTcp::kOpt_TTYP;
77const uint8_t Rw11VirtTermTcp::kOpt_LINE;
78
80
81//------------------------------------------+-----------------------------------
83
85 : Rw11VirtTerm(punit),
86 fFdListen(-1),
87 fFd(-1),
88 fState(ts_Closed),
89 fTcpTrace(false),
90 fSndPreConQue()
91{
92 fStats.Define(kStatNVTPreConSave , "NVTPreConSave" ,
93 "VT snd bytes saved prior connect");
94 fStats.Define(kStatNVTPreConDrop , "NVTPreConDrop" ,
95 "VT snd bytes dropped prior connect");
96 fStats.Define(kStatNVTListenPoll , "NVTListenPoll" ,
97 "VT ListenPollHandler() calls");
98 fStats.Define(kStatNVTAccept, "NVTAccept", "VT socket accepts");
99 fStats.Define(kStatNVTRcvRaw, "NVTRcvRaw", "VT raw bytes received");
100 fStats.Define(kStatNVTSndRaw, "NVTSndRaw", "VT raw bytes send");
101}
102
103//------------------------------------------+-----------------------------------
105
107{
108 if (fFdListen > 2) {
109 Rtools::Catch2Cerr(__func__,
110 [this](){ Server().RemovePollHandler(fFdListen); } );
111 ::close(fFdListen);
112 }
113 if (Connected()) {
114 Rtools::Catch2Cerr(__func__,
115 [this](){ Server().RemovePollHandler(fFd); } );
116 ::close(fFd);
117 }
118}
119
120//------------------------------------------+-----------------------------------
122
123bool Rw11VirtTermTcp::Open(const std::string& url, RerrMsg& emsg)
124{
125 if (!fUrl.Set(url, "|port=|trace|", "tcp", emsg)) return false;
126
127 string port;
128 if (!fUrl.FindOpt("port", port)) {
129 emsg.Init("Rw11VirtTermTcp::Open", "port= option not specified");
130 return false;
131 }
132
133 fTcpTrace = fUrl.FindOpt("trace");
134
135 unsigned long portno;
136 if (!Rtools::String2Long(port, portno, emsg)) return false;
137
138 protoent* pe = getprotobyname("tcp");
139 if (pe == nullptr) {
140 emsg.Init("Rw11VirtTermTcp::Open","getprotobyname(\"tcp\") failed");
141 return false;
142 }
143
144 int fd = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, pe->p_proto);
145 if (fd < 0) {
146 emsg.InitErrno("Rw11VirtTermTcp::Open","socket() failed: ", errno);
147 return false;
148 }
149
150 int on = 1;
151 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
152 emsg.InitErrno("Rw11VirtTermTcp::Open","setsockop() failed: ", errno);
153 ::close(fd);
154 return false;
155 }
156
157 sockaddr_in sa = {};
158 sa.sin_family = AF_INET;
159 sa.sin_port = htons(static_cast<unsigned short>(portno));
160 sa.sin_addr.s_addr = htonl(INADDR_ANY);
161
162 // Note: ::bind needed below to avoid collision with std::bind...
163 if (::bind(fd, reinterpret_cast<sockaddr*>(&sa), sizeof(sa)) < 0) {
164 emsg.InitErrno("Rw11VirtTermTcp::Open","bind() failed: ", errno);
165 ::close(fd);
166 return false;
167 }
168
169 if (listen(fd, 1) <0) {
170 emsg.InitErrno("Rw11VirtTermTcp::Open","listen() failed: ", errno);
171 ::close(fd);
172 return false;
173 }
174
175 fFdListen = fd;
176 fChannelId = port;
178
179 if (fTcpTrace) {
180 RlogMsg lmsg(LogFile(),'I');
181 lmsg << "TermTcp: listen on " << fChannelId << " for " << Unit().Name();
182 }
183
185 fFdListen, POLLIN);
186
187 return true;
188}
189
190//------------------------------------------+-----------------------------------
192
193bool Rw11VirtTermTcp::Snd(const uint8_t* data, size_t count, RerrMsg& /*emsg*/)
194{
196 const uint8_t* pdata = data;
197 const uint8_t* pdataend = data+count;
198 if (count == 0) return true; // quit if nothing to do
199
200 if (!Connected()) { // if not connected keep last chars
201 for (size_t i=0; i<count; i++) fSndPreConQue.push_back(data[i]);
202 fStats.Inc(kStatNVTPreConSave, double(count));
203 while (fSndPreConQue.size() > kPreConQue_limit) {
204 fSndPreConQue.pop_front();
206 }
207 return true;
208 }
209
210 const size_t c_bufsiz=1024;
211 uint8_t obuf[c_bufsiz];
212 while (pdata < pdataend) {
213 uint8_t* pobuf = obuf;
214 uint8_t* pobufend = obuf+sizeof(obuf);
215 while (pdata < pdataend && pobuf < pobufend-1) {
216 if (*pdata == kCode_IAC) *pobuf++ = kCode_IAC;
217 *pobuf++ = *pdata++;
218 }
219
220 int irc = ::write(fFd, obuf, pobuf-obuf);
221 if (irc < 0) {
222 RlogMsg lmsg(LogFile(),'E');
223 RerrMsg emsg("Rw11VirtTermTcp::Snd",
224 string("write() for port ") + fChannelId + " failed: ",
225 errno);
226 lmsg << emsg;
227 } else {
228 fStats.Inc(kStatNVTSndRaw, double(irc));
229 }
230 }
231
232 fStats.Inc(kStatNVTSndByt, double(count));
233 return true;
234}
235
236//------------------------------------------+-----------------------------------
238
239void Rw11VirtTermTcp::Dump(std::ostream& os, int ind, const char* text,
240 int detail) const
241{
242 RosFill bl(ind);
243 os << bl << (text?text:"--") << "Rw11VirtTermTcp @ " << this << endl;
244
245 os << bl << " fFdListen: " << fFdListen << endl;
246 os << bl << " fFd: " << fFd << endl;
247 const char* t_state = "";
248 switch (fState) {
249 case ts_Closed: t_state = "ts_Closed"; break;
250 case ts_Listen: t_state = "ts_Listen"; break;
251 case ts_Stream: t_state = "ts_Stream"; break;
252 case ts_Iac: t_state = "ts_Iac"; break;
253 case ts_Cmd: t_state = "ts_Cmd"; break;
254 case ts_Subneg: t_state = "ts_Subneg"; break;
255 case ts_Subiac: t_state = "ts_Subiac"; break;
256 default: t_state = "???";
257 }
258 os << bl << " fState: " << t_state << endl;
259 os << bl << " fTcpTrace: " << RosPrintf(fTcpTrace) << endl;
260 os << bl << " fSndPreConQue.size:" << fSndPreConQue.size() << endl;
261 Rw11VirtTerm::Dump(os, ind, " ^", detail);
262 return;
263}
264
265//------------------------------------------+-----------------------------------
267
269{
270 // bail-out and cancel handler if poll returns an error event
271 if (pfd.revents & (~pfd.events)) return -1;
272
273 fFd = ::accept(fFdListen, nullptr, 0);
274
275 if (fFd < 0) {
276 RlogMsg lmsg(LogFile(),'E');
277 RerrMsg emsg("Rw11VirtTermTcp::ListenPollHandler",
278 string("accept() for port ") + fChannelId + " failed: ",
279 errno);
280 lmsg << emsg;
281 // FIXME_code: proper error handling
282 return 0;
283 }
284
286
287 uint8_t buf_1[3] = {kCode_IAC, kCode_WILL, kOpt_LINE};
288 uint8_t buf_2[3] = {kCode_IAC, kCode_WILL, kOpt_SGA};
289 uint8_t buf_3[3] = {kCode_IAC, kCode_WILL, kOpt_ECHO};
290 uint8_t buf_4[3] = {kCode_IAC, kCode_WILL, kOpt_BIN};
291 uint8_t buf_5[3] = {kCode_IAC, kCode_DO , kOpt_BIN};
292
293 int nerr = 0;
294
295 // send initial negotiation WILLs and DOs
296 if (::write(fFd, buf_1, sizeof(buf_1)) < 0) nerr += 1;
297 if (::write(fFd, buf_2, sizeof(buf_2)) < 0) nerr += 1;
298 if (::write(fFd, buf_3, sizeof(buf_3)) < 0) nerr += 1;
299 if (::write(fFd, buf_4, sizeof(buf_4)) < 0) nerr += 1;
300 if (::write(fFd, buf_5, sizeof(buf_5)) < 0) nerr += 1;
301
302 // send connect message
303 if (nerr==0) {
304 stringstream msg;
305 msg << "\r\nconnect on port " << fChannelId
306 << " for " << Unit().Name() << "\r\n\r\n";
307 string str = msg.str();
308 if (::write(fFd, str.c_str(), str.length()) < 0) nerr += 1;
309 }
310
311 // send chars buffered while attached but not connected
312 if (nerr==0 && fSndPreConQue.size()) {
313 stringstream msg;
314 while (!fSndPreConQue.empty()) {
315 msg << char(fSndPreConQue.front());
316 fSndPreConQue.pop_front();
317 }
318 string str = msg.str();
319 if (::write(fFd, str.c_str(), str.length()) < 0) nerr += 1;
320 }
321
322 if (nerr) {
323 ::close(fFd);
324 fFd = -1;
325 RlogMsg lmsg(LogFile(),'E');
326 RerrMsg emsg("Rw11VirtTermTcp::ListenPollHandler",
327 string("initial write()s for port ") + fChannelId +
328 " failed: ", errno);
329 lmsg << emsg;
330 return 0;
331 }
332
333 if (fTcpTrace) {
334 RlogMsg lmsg(LogFile(),'I');
335 lmsg << "TermTcp: accept on " << fChannelId << " for " << Unit().Name();
336 }
337
339
342 fFd, POLLIN);
343 return 0;
344}
345
346//------------------------------------------+-----------------------------------
348
350{
352
353 int irc = -1;
354
355 if (pfd.revents & POLLIN) {
356 const size_t c_bufsiz=1024;
357 uint8_t ibuf[c_bufsiz];
358 uint8_t obuf[c_bufsiz];
359 uint8_t* pobuf = obuf;
360
361 irc = ::read(fFd, ibuf, sizeof(ibuf));
362
363 if (irc < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) return 0;
364
365 if (irc > 0) {
366 fStats.Inc(kStatNVTRcvRaw, double(irc));
367 for (int i=0; i<irc; i++) {
368 uint8_t byt = ibuf[i];
369 switch (fState) {
370 case ts_Stream:
371 if (byt == kCode_IAC) {
372 fState = ts_Iac;
373 } else {
374 *pobuf++ = byt;
376 }
377 break;
378
379 case ts_Iac:
380 if (byt == kCode_WILL || byt == kCode_WONT ||
381 byt == kCode_DO || byt == kCode_DONT) {
382 fState = ts_Cmd;
383 } else if (byt == kCode_SB) {
385 } else {
387 }
388 break;
389
390 case ts_Cmd:
392 break;
393
394 case ts_Subneg:
395 if (byt == kCode_IAC) {
397 }
398 break;
399
400 case ts_Subiac:
402 break;
403 default:
404 break;
405 }
406
407 }
408 }
409
410 if (pobuf > obuf) fRcvCb(obuf, pobuf - obuf);
411 }
412
413
414 if (irc <= 0) {
415 if (irc < 0) {
416 RlogMsg lmsg(LogFile(),'E');
417 RerrMsg emsg("Rw11VirtTermTcp::RcvPollHandler",
418 string("read() for port ") + fChannelId + " failed: ",
419 errno);
420 lmsg << emsg;
421 }
422 if (fTcpTrace) {
423 RlogMsg lmsg(LogFile(),'I');
424 lmsg << "TermTcp: close on " << fChannelId << " for " << Unit().Name();
425 }
426 ::close(fFd);
427 fFd = -1;
429 fFdListen, POLLIN);
431 return -1;
432 }
433
434 return 0;
435}
436
437
438} // end namespace Retro
FIXME_docs.
Definition: RerrMsg.hpp:25
void Init(const std::string &meth, const std::string &text)
FIXME_docs.
Definition: RerrMsg.cpp:74
void InitErrno(const std::string &meth, const std::string &text, int errnum)
FIXME_docs.
Definition: RerrMsg.cpp:84
void RemovePollHandler(int fd, short events, bool nothrow=false)
FIXME_docs.
void AddPollHandler(pollhdl_t &&pollhdl, int fd, short events=POLLIN)
FIXME_docs.
FIXME_docs.
Definition: RlogMsg.hpp:24
I/O appicator to generate fill characters.
Definition: RosFill.hpp:24
bool FindOpt(const std::string &name) const
FIXME_docs.
Definition: RparseUrl.cpp:196
bool Set(const std::string &url, const std::string &optlist, RerrMsg &emsg)
FIXME_docs.
Definition: RparseUrl.cpp:55
void Inc(size_t ind, double val=1.)
FIXME_docs.
Definition: Rstats.ipp:29
void Define(size_t ind, const std::string &name, const std::string &text)
FIXME_docs.
Definition: Rstats.cpp:72
FIXME_docs.
Definition: Rw11Unit.hpp:39
std::string Name() const
FIXME_docs.
Definition: Rw11Unit.ipp:31
static const uint8_t kCode_WILL
static const uint8_t kCode_DONT
Rw11VirtTermTcp(Rw11Unit *punit)
Default constructor.
static const uint8_t kCode_IP
static const size_t kPreConQue_limit
std::deque< uint8_t > fSndPreConQue
static const uint8_t kOpt_TTYP
static const uint8_t kCode_SB
static const uint8_t kOpt_LINE
bool Connected() const
FIXME_docs.
virtual bool Open(const std::string &url, RerrMsg &emsg)
FIXME_docs.
virtual void Dump(std::ostream &os, int ind=0, const char *text=0, int detail=0) const
FIXME_docs.
static const uint8_t kOpt_ECHO
static const uint8_t kCode_WONT
int RcvPollHandler(const pollfd &pfd)
FIXME_docs.
static const uint8_t kCode_NOP
static const uint8_t kCode_GA
static const uint8_t kCode_ESC
static const uint8_t kCode_NULL
static const uint8_t kCode_IAC
static const uint8_t kOpt_SGA
int ListenPollHandler(const pollfd &pfd)
FIXME_docs.
static const uint8_t kCode_DO
virtual bool Snd(const uint8_t *data, size_t count, RerrMsg &emsg)
FIXME_docs.
static const uint8_t kCode_SE
static const uint8_t kOpt_BIN
static const uint8_t kCode_LF
static const uint8_t kCode_CR
rcvcbfo_t fRcvCb
receive callback fobj
std::string fChannelId
channel id
virtual void Dump(std::ostream &os, int ind=0, const char *text=0, int detail=0) const
FIXME_docs.
RparseUrl fUrl
Definition: Rw11Virt.hpp:66
Rstats fStats
statistics
Definition: Rw11Virt.hpp:68
RlogFile & LogFile() const
FIXME_docs.
Definition: Rw11Virt.ipp:63
Rw11Unit & Unit() const
FIXME_docs.
Definition: Rw11Virt.ipp:23
RlinkServer & Server() const
FIXME_docs.
Definition: Rw11Virt.ipp:55
RosPrintfS< bool > RosPrintf(bool value, const char *form=0, int width=0, int prec=0)
Creates a print object for the formatted output of a bool value.
Definition: RosPrintf.ipp:38
bool String2Long(const std::string &str, long &res, RerrMsg &emsg, int base)
FIXME_docs.
Definition: Rtools.cpp:67
void Catch2Cerr(const char *msg, std::function< void()> func)
FIXME_docs.
Definition: Rtools.cpp:170
Declaration of class ReventLoop.
Definition: ReventLoop.cpp:47