OpenCores
URL https://opencores.org/ocsvn/w11/w11/trunk

Subversion Repositories w11

[/] [w11/] [tags/] [w11a_V0.61/] [tools/] [src/] [librw11/] [Rw11VirtTermTcp.cpp] - Blame information for rev 26

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 22 wfjm
// $Id: Rw11VirtTermTcp.cpp 521 2013-05-20 22:16:45Z mueller $
2 19 wfjm
//
3
// Copyright 2013- by Walter F.J. Mueller <W.F.J.Mueller@gsi.de>
4
//
5
// This program is free software; you may redistribute and/or modify it under
6
// the terms of the GNU General Public License as published by the Free
7
// Software Foundation, either version 2, or at your option any later version.
8
//
9
// This program is distributed in the hope that it will be useful, but
10
// WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
11
// or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12
// for complete details.
13
// 
14
// Revision History: 
15
// Date         Rev Version  Comment
16 22 wfjm
// 2013-05-17   512   1.0.3  use Rtools::String2Long
17 21 wfjm
// 2013-05-05   516   1.0.2  fix mistakes in emsg generation with errno
18 20 wfjm
// 2013-04-20   508   1.0.1  add fSndPreConQue handling
19 19 wfjm
// 2013-03-06   495   1.0    Initial version
20
// 2013-02-13   488   0.1    First draft
21
// ---------------------------------------------------------------------------
22
 
23
/*!
24
  \file
25 22 wfjm
  \version $Id: Rw11VirtTermTcp.cpp 521 2013-05-20 22:16:45Z mueller $
26 19 wfjm
  \brief   Implemenation of Rw11VirtTermTcp.
27
*/
28
 
29
#include <sys/types.h>
30
#include <sys/socket.h>
31
#include <netdb.h>
32
#include <string.h>
33
 
34
#include <sstream>
35
 
36
#include "librtools/RosFill.hpp"
37
#include "librtools/RlogMsg.hpp"
38
 
39
#include "Rw11VirtTermTcp.hpp"
40
 
41
using namespace std;
42
 
43
/*!
44
  \class Retro::Rw11VirtTermTcp
45
  \brief FIXME_docs
46
*/
47
 
48
// all method definitions in namespace Retro
49
namespace Retro {
50
 
51
//------------------------------------------+-----------------------------------
52
// constants definitions
53
 
54
const uint8_t  Rw11VirtTermTcp::kCode_NULL;
55
const uint8_t  Rw11VirtTermTcp::kCode_LF;
56
const uint8_t  Rw11VirtTermTcp::kCode_CR;
57
const uint8_t  Rw11VirtTermTcp::kCode_ESC;
58
const uint8_t  Rw11VirtTermTcp::kCode_SE;
59
const uint8_t  Rw11VirtTermTcp::kCode_NOP;
60
const uint8_t  Rw11VirtTermTcp::kCode_IP;
61
const uint8_t  Rw11VirtTermTcp::kCode_GA;
62
const uint8_t  Rw11VirtTermTcp::kCode_SB;
63
const uint8_t  Rw11VirtTermTcp::kCode_WILL;
64
const uint8_t  Rw11VirtTermTcp::kCode_WONT;
65
const uint8_t  Rw11VirtTermTcp::kCode_DO;
66
const uint8_t  Rw11VirtTermTcp::kCode_DONT;
67
const uint8_t  Rw11VirtTermTcp::kCode_IAC;
68
 
69
const uint8_t  Rw11VirtTermTcp::kOpt_BIN;
70
const uint8_t  Rw11VirtTermTcp::kOpt_ECHO;
71
const uint8_t  Rw11VirtTermTcp::kOpt_SGA;
72
const uint8_t  Rw11VirtTermTcp::kOpt_TTYP;
73
const uint8_t  Rw11VirtTermTcp::kOpt_LINE;
74
 
75 20 wfjm
const size_t   Rw11VirtTermTcp::kPreConQue_limit;
76
 
77 19 wfjm
//------------------------------------------+-----------------------------------
78
//! Default constructor
79
 
80
Rw11VirtTermTcp::Rw11VirtTermTcp(Rw11Unit* punit)
81
  : Rw11VirtTerm(punit),
82
    fFdListen(-1),
83
    fFd(-1),
84
    fState(ts_Closed),
85 20 wfjm
    fTcpTrace(false),
86
    fSndPreConQue()
87 19 wfjm
{
88 20 wfjm
  fStats.Define(kStatNVTPreConSave , "NVTPreConSave" ,
89
                "VT snd bytes saved prior connect");
90
  fStats.Define(kStatNVTPreConDrop , "NVTPreConDrop" ,
91
                "VT snd bytes dropped prior connect");
92 19 wfjm
  fStats.Define(kStatNVTListenPoll , "NVTListenPoll" ,
93 20 wfjm
                "VT ListenPollHandler() calls");
94
  fStats.Define(kStatNVTAccept,      "NVTAccept",     "VT socket accepts");
95
  fStats.Define(kStatNVTRcvRaw,      "NVTRcvRaw",     "VT raw bytes received");
96
  fStats.Define(kStatNVTSndRaw,      "NVTSndRaw",     "VT raw bytes send");
97 19 wfjm
}
98
 
99
//------------------------------------------+-----------------------------------
100
//! Destructor
101
 
102
Rw11VirtTermTcp::~Rw11VirtTermTcp()
103
{
104
  if (fFdListen > 2) {
105
    Server().RemovePollHandler(fFdListen);
106
    close(fFdListen);
107
  }
108 20 wfjm
  if (Connected()) {
109 19 wfjm
    Server().RemovePollHandler(fFd);
110
    close(fFd);
111
  }
112
}
113
 
114
//------------------------------------------+-----------------------------------
115
//! FIXME_docs
116
 
117
bool Rw11VirtTermTcp::Open(const std::string& url, RerrMsg& emsg)
118
{
119
  if (!fUrl.Set(url, "|port=|trace|", emsg)) return false;
120
  if (!(fUrl.FindOpt("port"))) {
121
    emsg.Init("Rw11VirtTermTcp::Open", "port= option not specified");
122
    return false;
123
  }
124
 
125
  fTcpTrace = fUrl.FindOpt("trace");
126
 
127
  string port;
128
  fUrl.FindOpt("port",port);
129 22 wfjm
  unsigned long portno;
130
  if (!Rtools::String2Long(port, portno, emsg)) return false;
131 19 wfjm
 
132
  protoent* pe = getprotobyname("tcp");
133
  if (pe == 0) {
134
    emsg.Init("Rw11VirtTermTcp::Open","getprotobyname(\"tcp\") failed");
135
    return false;
136
  }
137
 
138
  int fd = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, pe->p_proto);
139
  if (fd < 0) {
140
    emsg.InitErrno("Rw11VirtTermTcp::Open","socket() failed: ", errno);
141
    return false;
142
  }
143
 
144
  int on = 1;
145
  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
146
    emsg.InitErrno("Rw11VirtTermTcp::Open","setsockop() failed: ", errno);
147
    close(fd);
148
    return false;
149
  }
150
 
151
  sockaddr_in sa;
152
  memset(&sa, 0, sizeof(sa));
153
  sa.sin_family = AF_INET;
154
  sa.sin_port   = htons((unsigned short) portno);
155
  sa.sin_addr.s_addr = htonl(INADDR_ANY);
156
 
157
  // Note: ::bind needed below to avoid collision with std::bind... 
158
  if (::bind(fd, (sockaddr*) &sa, sizeof(sa)) < 0) {
159
    emsg.InitErrno("Rw11VirtTermTcp::Open","bind() failed: ", errno);
160
    close(fd);
161
    return false;
162
  }
163
 
164
  if (listen(fd, 1) <0) {
165
    emsg.InitErrno("Rw11VirtTermTcp::Open","listen() failed: ", errno);
166
    close(fd);
167
    return false;
168
  }
169
 
170
  fFdListen = fd;
171
  fChannelId = port;
172
  fState = ts_Listen;
173
 
174
  if (fTcpTrace) {
175
    RlogMsg lmsg(LogFile(),'I');
176
    lmsg << "TermTcp: listen on " << fChannelId << " for " << Unit().Name();
177
  }
178
 
179
  Server().AddPollHandler(boost::bind(&Rw11VirtTermTcp::ListenPollHandler,
180
                                      this, _1),
181
                          fFdListen, POLLIN);
182
 
183
  return true;
184
}
185
 
186
//------------------------------------------+-----------------------------------
187
//! FIXME_docs
188
 
189
bool Rw11VirtTermTcp::Snd(const uint8_t* data, size_t count, RerrMsg& emsg)
190
{
191
  fStats.Inc(kStatNVTSnd);
192
  const uint8_t* pdata = data;
193
  const uint8_t* pdataend = data+count;
194 20 wfjm
  if (count == 0) return true;              // quit if nothing to do
195 19 wfjm
 
196 20 wfjm
  if (!Connected()) {                       // if not connected keep last chars
197
    for (size_t i=0; i<count; i++) fSndPreConQue.push_back(data[i]);
198
    fStats.Inc(kStatNVTPreConSave, double(count));
199
    while (fSndPreConQue.size() > kPreConQue_limit) {
200
      fSndPreConQue.pop_front();
201
      fStats.Inc(kStatNVTPreConDrop);
202
    }
203
    return true;
204
  }
205
 
206 19 wfjm
  uint8_t  obuf[1024];
207
  while (pdata < pdataend) {
208
    uint8_t* pobuf = obuf;
209
    uint8_t* pobufend = obuf+1024;
210
    while (pdata < pdataend && pobuf < pobufend-1) {
211
      if (*pdata == kCode_IAC) *pobuf++ = kCode_IAC;
212
      *pobuf++ = *pdata++;
213
    }
214
 
215
    int irc = write(fFd, obuf, pobuf-obuf);
216
    if (irc < 0) {
217
      RlogMsg lmsg(LogFile(),'E');
218
      RerrMsg emsg("Rw11VirtTermTcp::Snd",
219 21 wfjm
                   string("write() for port ") + fChannelId + " failed: ",
220
                   errno);
221 19 wfjm
      lmsg << emsg;
222
    } else {
223
      fStats.Inc(kStatNVTSndRaw, double(irc));
224
    }
225
  }
226
 
227
  fStats.Inc(kStatNVTSndByt, double(count));
228
  return true;
229
}
230
 
231
//------------------------------------------+-----------------------------------
232
//! FIXME_docs
233
 
234
void Rw11VirtTermTcp::Dump(std::ostream& os, int ind, const char* text) const
235
{
236
  RosFill bl(ind);
237
  os << bl << (text?text:"--") << "Rw11VirtTermTcp @ " << this << endl;
238
 
239
  os << bl << "  fFdListen:       " << fFdListen << endl;
240
  os << bl << "  fFd:             " << fFd << endl;
241
  const char* t_state = "";
242
  switch (fState) {
243
  case ts_Closed: t_state = "ts_Closed";  break;
244
  case ts_Listen: t_state = "ts_Listen";  break;
245
  case ts_Stream: t_state = "ts_Stream";  break;
246
  case ts_Iac:    t_state = "ts_Iac";     break;
247
  case ts_Cmd:    t_state = "ts_Cmd";     break;
248
  case ts_Subneg: t_state = "ts_Subneg";  break;
249
  case ts_Subiac: t_state = "ts_Subiac";  break;
250
  default: t_state = "???";
251
  }
252
  os << bl << "  fState:          " << t_state    << endl;
253
  os << bl << "  fTcpTrace:       " << fTcpTrace  << endl;
254 20 wfjm
  os << bl << "  fSndPreConQue.size" << fSndPreConQue.size()  << endl;
255 19 wfjm
  Rw11VirtTerm::Dump(os, ind, " ^");
256
  return;
257
}
258
 
259
//------------------------------------------+-----------------------------------
260
//! FIXME_docs
261
 
262
int Rw11VirtTermTcp::ListenPollHandler(const pollfd& pfd)
263
{
264
  // bail-out and cancel handler if poll returns an error event
265
  if (pfd.revents & (~pfd.events)) return -1;
266
 
267
  fFd = accept(fFdListen, NULL, 0);
268
 
269
  if (fFd < 0) {
270
    RlogMsg lmsg(LogFile(),'E');
271
    RerrMsg emsg("Rw11VirtTermTcp::ListenPollHandler",
272 21 wfjm
                 string("accept() for port ") + fChannelId + " failed: ",
273
                 errno);
274 19 wfjm
    lmsg << emsg;
275
    // FIXME_code: proper error handling
276
    return 0;
277
  }
278
 
279
  fStats.Inc(kStatNVTAccept);
280
 
281
  uint8_t buf_1[3] = {kCode_IAC, kCode_WILL, kOpt_LINE};
282
  uint8_t buf_2[3] = {kCode_IAC, kCode_WILL, kOpt_SGA};
283
  uint8_t buf_3[3] = {kCode_IAC, kCode_WILL, kOpt_ECHO};
284
  uint8_t buf_4[3] = {kCode_IAC, kCode_WILL, kOpt_BIN};
285
  uint8_t buf_5[3] = {kCode_IAC, kCode_DO  , kOpt_BIN};
286
 
287
  int nerr = 0;
288
 
289 20 wfjm
  // send initial negotiation WILLs and DOs
290 19 wfjm
  if (write(fFd, buf_1, sizeof(buf_1)) < 0) nerr += 1;
291
  if (write(fFd, buf_2, sizeof(buf_2)) < 0) nerr += 1;
292
  if (write(fFd, buf_3, sizeof(buf_3)) < 0) nerr += 1;
293
  if (write(fFd, buf_4, sizeof(buf_4)) < 0) nerr += 1;
294
  if (write(fFd, buf_5, sizeof(buf_5)) < 0) nerr += 1;
295 20 wfjm
 
296
  // send connect message
297 19 wfjm
  if (nerr==0) {
298
    stringstream msg;
299
    msg << "\r\nconnect on port " << fChannelId
300
        << " for " << Unit().Name() << "\r\n\r\n";
301
    string str = msg.str();
302
    if (write(fFd, str.c_str(), str.length()) < 0) nerr += 1;
303
  }
304
 
305 20 wfjm
  // send chars buffered while attached but not connected
306
  if (nerr==0 && fSndPreConQue.size()) {
307
    stringstream msg;
308
    while (!fSndPreConQue.empty()) {
309
      msg << char(fSndPreConQue.front());
310
      fSndPreConQue.pop_front();
311
    }
312
    string str = msg.str();
313
    if (write(fFd, str.c_str(), str.length()) < 0) nerr += 1;
314
  }
315
 
316 19 wfjm
  if (nerr) {
317
    close(fFd);
318
    fFd = -1;
319
    RlogMsg lmsg(LogFile(),'E');
320
    RerrMsg emsg("Rw11VirtTermTcp::ListenPollHandler",
321 21 wfjm
                 string("initial write()s for port ") + fChannelId +
322
                 " failed: ", errno);
323 19 wfjm
    lmsg << emsg;
324
    return 0;
325
  }
326
 
327
  if (fTcpTrace) {
328
    RlogMsg lmsg(LogFile(),'I');
329
    lmsg << "TermTcp: accept on " << fChannelId << " for " << Unit().Name();
330
  }
331
 
332
  fState = ts_Stream;
333
 
334
  Server().RemovePollHandler(fFdListen);
335
  Server().AddPollHandler(boost::bind(&Rw11VirtTermTcp::RcvPollHandler,
336
                                      this, _1),
337
                          fFd, POLLIN);
338
  return 0;
339
}
340
 
341
//------------------------------------------+-----------------------------------
342
//! FIXME_docs
343
 
344
int Rw11VirtTermTcp::RcvPollHandler(const pollfd& pfd)
345
{
346
  fStats.Inc(kStatNVTRcvPoll);
347
 
348
  int irc = -1;
349
 
350
  if (pfd.revents & POLLIN) {
351
    uint8_t ibuf[1024];
352
    uint8_t obuf[1024];
353
    uint8_t* pobuf = obuf;
354
 
355
    irc = read(fFd, ibuf, 1024);
356
 
357
    if (irc < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) return 0;
358
 
359
    if (irc > 0) {
360
      fStats.Inc(kStatNVTRcvRaw, double(irc));
361
      for (int i=0; i<irc; i++) {
362
        uint8_t byt = ibuf[i];
363
        switch (fState) {
364
        case ts_Stream:
365
          if (byt == kCode_IAC) {
366
            fState = ts_Iac;
367
          } else {
368
            *pobuf++ = byt;
369
            fStats.Inc(kStatNVTRcvByt, 1.);
370
          }
371
          break;
372
 
373
        case ts_Iac:
374
          if (byt == kCode_WILL || byt == kCode_WONT ||
375
              byt == kCode_DO   || byt == kCode_DONT) {
376
            fState = ts_Cmd;
377
          } else if (byt == kCode_SB) {
378
            fState = ts_Subneg;
379
          } else {
380
            fState = ts_Stream;
381
          }
382
          break;
383
 
384
        case ts_Cmd:
385
          fState = ts_Stream;
386
          break;
387
 
388
        case ts_Subneg:
389
          if (byt == kCode_IAC) {
390
            fState = ts_Subiac;
391
          }
392
          break;
393
 
394
        case ts_Subiac:
395
          fState = ts_Stream;
396
          break;
397
        default:
398
          break;
399
        }
400
 
401
      }
402
    }
403
 
404
    if (pobuf > obuf) fRcvCb(obuf, pobuf - obuf);
405
  }
406
 
407
 
408
  if (irc <= 0) {
409
    if (irc < 0) {
410
      RlogMsg lmsg(LogFile(),'E');
411
      RerrMsg emsg("Rw11VirtTermTcp::ListenPollHandler",
412 21 wfjm
                   string("read() for port ") + fChannelId + " failed: ",
413
                   errno);
414 19 wfjm
      lmsg << emsg;
415
    }
416
    if (fTcpTrace) {
417
      RlogMsg lmsg(LogFile(),'I');
418
      lmsg << "TermTcp: close on " << fChannelId << " for " << Unit().Name();
419
    }
420
    close(fFd);
421
    fFd = -1;
422
    Server().AddPollHandler(boost::bind(&Rw11VirtTermTcp::ListenPollHandler,
423
                                        this, _1),
424
                            fFdListen, POLLIN);
425
    fState = ts_Listen;
426
    return -1;
427
  }
428
 
429
  return 0;
430
}
431
 
432
 
433
} // end namespace Retro

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.