Coverage Report

Created: 2026-06-15 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/logging-log4cxx/src/main/cpp/telnetappender.cpp
Line
Count
Source
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
#include <log4cxx/net/telnetappender.h>
19
#include <log4cxx/helpers/loglog.h>
20
#include <log4cxx/helpers/optionconverter.h>
21
#include <log4cxx/helpers/stringhelper.h>
22
#include <log4cxx/helpers/serversocket.h>
23
#include <log4cxx/helpers/charsetencoder.h>
24
#include <log4cxx/helpers/bytebuffer.h>
25
#include <log4cxx/helpers/threadutility.h>
26
#include <log4cxx/private/appenderskeleton_priv.h>
27
#if LOG4CXX_ABI_VERSION <= 15
28
#include <log4cxx/private/aprsocket.h>
29
#endif
30
#include <mutex>
31
#include <thread>
32
#include <vector>
33
34
#if LOG4CXX_EVENTS_AT_EXIT
35
#include <log4cxx/private/atexitregistry.h>
36
#endif
37
38
using namespace LOG4CXX_NS;
39
using namespace LOG4CXX_NS::helpers;
40
using namespace LOG4CXX_NS::net;
41
42
struct Connection
43
{
44
  helpers::SocketPtr s;
45
  size_t sentCount;
46
};
47
LOG4CXX_LIST_DEF(ConnectionList, Connection);
48
49
IMPLEMENT_LOG4CXX_OBJECT(TelnetAppender)
50
51
struct TelnetAppender::TelnetAppenderPriv : public AppenderSkeletonPrivate
52
{
53
0
  TelnetAppenderPriv( int port, int maxConnections ) : AppenderSkeletonPrivate(),
54
0
    port(port),
55
0
    connections(maxConnections),
56
0
    encoding(LOG4CXX_STR("UTF-8")),
57
0
    encoder(CharsetEncoder::getUTF8Encoder()),
58
0
    activeConnections(0)
59
#if LOG4CXX_EVENTS_AT_EXIT
60
    , atExitRegistryRaii([this]
61
    {
62
      if (setClosed())
63
        stopAcceptingConnections();
64
    })
65
#endif
66
0
  { }
67
68
  ~TelnetAppenderPriv()
69
0
  { stopAcceptingConnections(); }
70
71
  int port;
72
  LogString hostname;
73
  bool reuseAddress = false;
74
  bool nonBlocking = false;
75
  ConnectionList connections;
76
  LogString encoding;
77
  LOG4CXX_NS::helpers::CharsetEncoderPtr encoder;
78
  std::unique_ptr<helpers::ServerSocket> serverSocket;
79
  std::thread sh;
80
  size_t activeConnections;
81
  size_t eventCount{ 0 };
82
83
#if LOG4CXX_EVENTS_AT_EXIT
84
  helpers::AtExitRegistry::Raii atExitRegistryRaii;
85
#endif
86
87
  void stopAcceptingConnections()
88
0
  {
89
0
    if (!this->serverSocket)
90
0
      return;
91
    // Interrupt accept()
92
0
    try
93
0
    {
94
0
      this->serverSocket->close();
95
0
    }
96
0
    catch (Exception&)
97
0
    {
98
0
    }
99
0
    if (this->sh.joinable())
100
0
      this->sh.join();
101
0
  }
102
103
  void close();
104
};
105
106
0
#define _priv static_cast<TelnetAppenderPriv*>(m_priv.get())
107
108
/** The default telnet server port */
109
const int TelnetAppender::DEFAULT_PORT = 23;
110
111
/** The maximum number of concurrent connections */
112
const int TelnetAppender::MAX_CONNECTIONS = 20;
113
114
TelnetAppender::TelnetAppender()
115
0
  : AppenderSkeleton (std::make_unique<TelnetAppenderPriv>(DEFAULT_PORT, MAX_CONNECTIONS))
116
0
{
117
0
}
Unexecuted instantiation: log4cxx::net::TelnetAppender::TelnetAppender()
Unexecuted instantiation: log4cxx::net::TelnetAppender::TelnetAppender()
118
119
TelnetAppender::~TelnetAppender()
120
0
{
121
0
  if (_priv->setClosed())
122
0
    _priv->close();
123
0
}
124
125
void TelnetAppender::activateOptions( LOG4CXX_ACTIVATE_OPTIONS_FORMAL_PARAMETERS )
126
0
{
127
0
  if (_priv->serverSocket == NULL)
128
0
  {
129
0
    _priv->serverSocket = ServerSocket::create(_priv->port, _priv->reuseAddress, _priv->hostname);
130
0
    _priv->serverSocket->setSoTimeout(1000);
131
0
  }
132
133
0
  if (!_priv->sh.joinable())
134
0
    _priv->sh = ThreadUtility::instance()->createThread( LOG4CXX_STR("TelnetAppender"), &TelnetAppender::acceptConnections, this );
135
0
}
136
137
void TelnetAppender::setOption(const LogString& option,
138
  const LogString& value)
139
0
{
140
0
  if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("PORT"), LOG4CXX_STR("port")))
141
0
  {
142
0
    setPort(OptionConverter::toInt(value, DEFAULT_PORT));
143
0
  }
144
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("MAXCONNECTIONS"), LOG4CXX_STR("maxconnections")))
145
0
  {
146
0
    setMaxConnections(OptionConverter::toInt(value, MAX_CONNECTIONS));
147
0
  }
148
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("ENCODING"), LOG4CXX_STR("encoding")))
149
0
  {
150
0
    setEncoding(value);
151
0
  }
152
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("NONBLOCKING"), LOG4CXX_STR("nonblocking")))
153
0
  {
154
0
    setNonBlocking(OptionConverter::toBoolean(value, true));
155
0
  }
156
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("REUSEADDRESS"), LOG4CXX_STR("reuseaddress")))
157
0
  {
158
0
    setReuseAddress(OptionConverter::toBoolean(value, true));
159
0
  }
160
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("HOSTNAME"), LOG4CXX_STR("hostname")))
161
0
  {
162
0
    setHostname(value);
163
0
  }
164
0
  else
165
0
  {
166
0
    AppenderSkeleton::setOption(option, value);
167
0
  }
168
0
}
Unexecuted instantiation: log4cxx::net::TelnetAppender::setOption(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::net::TelnetAppender::setOption(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&, std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
169
170
LogString TelnetAppender::getEncoding() const
171
0
{
172
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
173
0
  return _priv->encoding;
174
0
}
175
176
void TelnetAppender::setEncoding(const LogString& value)
177
0
{
178
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
179
0
  _priv->encoder = CharsetEncoder::getEncoder(value);
180
0
  _priv->encoding = value;
181
0
}
Unexecuted instantiation: log4cxx::net::TelnetAppender::setEncoding(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::net::TelnetAppender::setEncoding(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
182
183
184
void TelnetAppender::close()
185
0
{
186
0
  if (_priv->setClosed())
187
0
    _priv->close();
188
0
}
189
190
void TelnetAppender::TelnetAppenderPriv::close()
191
0
{
192
0
  this->stopAcceptingConnections();
193
0
  std::lock_guard<std::recursive_mutex> lock(this->mutex);
194
0
  if (this->eventCount && helpers::LogLog::isDebugEnabled())
195
0
  {
196
0
    LogString msg = LOG4CXX_STR("TelnetAppender eventCount ");
197
0
    helpers::StringHelper::toString(this->eventCount, msg);
198
0
    helpers::LogLog::debug(msg);
199
0
  }
200
0
  SocketPtr nullSocket;
201
0
  int connectionNumber{ 0 };
202
0
  for (auto& item : this->connections)
203
0
  {
204
0
    ++connectionNumber;
205
0
    if (item.s)
206
0
    {
207
0
      item.s->close();
208
0
      if (this->eventCount && helpers::LogLog::isDebugEnabled())
209
0
      {
210
0
        LogString msg = LOG4CXX_STR("TelnetAppender connection ");
211
0
        helpers::StringHelper::toString(connectionNumber, msg);
212
0
        msg += LOG4CXX_STR(" sentCount ");
213
0
        helpers::StringHelper::toString(item.sentCount, msg);
214
0
        helpers::LogLog::debug(msg);
215
0
      }
216
0
      item = Connection{ nullSocket, 0 };
217
0
    }
218
0
  }
219
0
  this->activeConnections = 0;
220
0
}
221
222
223
void TelnetAppender::write(ByteBuffer& buf)
224
0
{
225
0
  int connectionNumber{ 0 };
226
0
  for (auto& item :_priv->connections)
227
0
  {
228
0
    ++connectionNumber;
229
0
    if (item.s)
230
0
    {
231
0
      try
232
0
      {
233
0
        ByteBuffer b(buf.current(), buf.remaining());
234
0
        item.s->write(b);
235
0
        ++item.sentCount;
236
0
      }
237
0
      catch (const Exception& e)
238
0
      {
239
0
        if (helpers::LogLog::isDebugEnabled())
240
0
        {
241
0
          LogString msg(LOG4CXX_STR("TelnetAppender connection "));
242
0
          helpers::StringHelper::toString(connectionNumber, msg);
243
0
          msg += LOG4CXX_STR(" sentCount ");
244
0
          helpers::StringHelper::toString(item.sentCount, msg);
245
0
          msg += LOG4CXX_STR("/");
246
0
          helpers::StringHelper::toString(_priv->eventCount, msg);
247
0
          helpers::LogLog::warn(msg, e);
248
0
        }
249
0
        item.s.reset();
250
0
        _priv->activeConnections--;
251
0
      }
252
0
    }
253
0
  }
254
0
}
255
256
void TelnetAppender::writeStatus(const SocketPtr& socket, const LogString& msg, Pool& p)
257
0
{
258
0
  size_t bytesSize = msg.size() * 2;
259
0
  char* bytes = p.pstralloc(bytesSize);
260
261
0
  LogString::const_iterator msgIter(msg.begin());
262
0
  ByteBuffer buf(bytes, bytesSize);
263
264
0
  while (msgIter != msg.end())
265
0
  {
266
0
    _priv->encoder->encode(msg, msgIter, buf);
267
0
    buf.flip();
268
0
    socket->write(buf);
269
0
    buf.clear();
270
0
  }
271
0
}
Unexecuted instantiation: log4cxx::net::TelnetAppender::writeStatus(std::__1::shared_ptr<log4cxx::helpers::Socket> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, log4cxx::helpers::Pool&)
Unexecuted instantiation: log4cxx::net::TelnetAppender::writeStatus(std::__1::shared_ptr<log4cxx::helpers::Socket> const&, std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&, log4cxx::helpers::Pool&)
272
273
void TelnetAppender::append( LOG4CXX_APPEND_FORMAL_PARAMETERS )
274
0
{
275
0
  ++_priv->eventCount;
276
0
  if (0 < _priv->activeConnections)
277
0
  {
278
0
    helpers::Pool tempPool;
279
0
    LogString msg;
280
0
    if (_priv->layout)
281
0
      _priv->layout->format(msg, event);
282
0
    else
283
0
      msg = event->getRenderedMessage();
284
0
    msg.append(LOG4CXX_STR("\r\n"));
285
0
    size_t bytesSize = msg.size() * 2;
286
0
    char* bytes = tempPool.pstralloc(bytesSize);
287
288
0
    LogString::const_iterator msgIter(msg.begin());
289
0
    ByteBuffer buf(bytes, bytesSize);
290
291
0
    std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
292
293
0
    while (msgIter != msg.end())
294
0
    {
295
0
      log4cxx_status_t stat = _priv->encoder->encode(msg, msgIter, buf);
296
0
      buf.flip();
297
0
      write(buf);
298
0
      buf.clear();
299
300
0
      if (CharsetEncoder::isError(stat))
301
0
      {
302
0
        LogString unrepresented(1, 0x3F /* '?' */);
303
0
        LogString::const_iterator unrepresentedIter(unrepresented.begin());
304
0
        stat = _priv->encoder->encode(unrepresented, unrepresentedIter, buf);
305
0
        buf.flip();
306
0
        write(buf);
307
0
        buf.clear();
308
0
        msgIter++;
309
0
      }
310
0
    }
311
0
  }
312
0
}
313
314
void TelnetAppender::acceptConnections()
315
0
{
316
317
  // main loop; is left when This->closed is != 0 after an accept()
318
0
  while (true)
319
0
  {
320
0
    try
321
0
    {
322
0
      SocketPtr newClient = _priv->serverSocket->accept();
323
#if 15 < LOG4CXX_ABI_VERSION
324
      newClient->setNonBlocking(_priv->nonBlocking);
325
#else
326
0
      if (auto p = dynamic_cast<APRSocket*>(newClient.get()))
327
0
        p->setNonBlocking(_priv->nonBlocking);
328
0
#endif
329
0
      bool done = _priv->closed;
330
331
0
      if (done)
332
0
      {
333
0
        Pool p;
334
0
        writeStatus(newClient, LOG4CXX_STR("Log closed.\r\n"), p);
335
0
        newClient->close();
336
337
0
        break;
338
0
      }
339
340
0
      size_t count = _priv->activeConnections;
341
342
0
      if (count >= _priv->connections.size())
343
0
      {
344
0
        Pool p;
345
0
        writeStatus(newClient, LOG4CXX_STR("Too many connections.\r\n"), p);
346
0
        newClient->close();
347
0
      }
348
0
      else
349
0
      {
350
        //
351
        //   find unoccupied connection
352
        //
353
0
        std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
354
355
0
        int connectionNumber{ 0 };
356
0
        for (auto& item : _priv->connections)
357
0
        {
358
0
          ++connectionNumber;
359
0
          if (!item.s)
360
0
          {
361
0
            item = Connection{ newClient, 0 };
362
0
            _priv->activeConnections++;
363
0
            if (helpers::LogLog::isDebugEnabled())
364
0
            {
365
0
              LogString msg = LOG4CXX_STR("TelnetAppender new connection ");
366
0
              helpers::StringHelper::toString(connectionNumber, msg);
367
0
              msg += LOG4CXX_STR("/");
368
0
              helpers::StringHelper::toString(_priv->activeConnections, msg);
369
0
              helpers::LogLog::debug(msg);
370
0
            }
371
372
0
            break;
373
0
          }
374
0
        }
375
376
0
        Pool p;
377
0
        LogString oss(LOG4CXX_STR("TelnetAppender v1.0 ("));
378
0
        StringHelper::toString((int) count + 1, oss);
379
0
        oss += LOG4CXX_STR(" active connections)\r\n\r\n");
380
0
        writeStatus(newClient, oss, p);
381
0
      }
382
0
    }
383
0
    catch (InterruptedIOException&)
384
0
    {
385
0
      if (_priv->closed)
386
0
      {
387
0
        break;
388
0
      }
389
0
    }
390
0
    catch (Exception& e)
391
0
    {
392
0
      if (!_priv->closed)
393
0
      {
394
0
        LogLog::error(LOG4CXX_STR("Encountered error while in SocketHandler loop."), e);
395
0
      }
396
0
      else
397
0
      {
398
0
        break;
399
0
      }
400
0
    }
401
0
  }
402
403
0
}
404
405
int TelnetAppender::getPort() const
406
0
{
407
0
  return _priv->port;
408
0
}
409
410
void TelnetAppender::setPort(int port1)
411
0
{
412
0
  _priv->port = port1;
413
0
}
414
415
LogString TelnetAppender::getHostname() const
416
0
{
417
0
  return _priv->hostname;
418
0
}
419
420
void TelnetAppender::setHostname(const LogString& hostname)
421
0
{
422
0
  _priv->hostname = hostname;
423
0
}
Unexecuted instantiation: log4cxx::net::TelnetAppender::setHostname(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::net::TelnetAppender::setHostname(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
424
425
int TelnetAppender::getMaxConnections() const
426
0
{
427
0
  return static_cast<int>(_priv->connections.size());
428
0
}
429
430
void TelnetAppender::setMaxConnections(int newValue)
431
0
{
432
0
  if (newValue < 0)
433
0
  {
434
0
    LogLog::warn(LOG4CXX_STR("TelnetAppender MaxConnections must be non-negative. Keeping the previous value."));
435
0
    return;
436
0
  }
437
438
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
439
0
  const size_t newSize = static_cast<size_t>(newValue);
440
0
  if (_priv->connections.size() < newSize)
441
0
    _priv->connections.resize(newSize);
442
0
  else while (newSize < _priv->connections.size())
443
0
  {
444
0
    auto item = _priv->connections.back();
445
0
    _priv->connections.pop_back();
446
0
    if (item.s)
447
0
    {
448
0
      item.s->close();
449
0
      --_priv->activeConnections;
450
0
    }
451
0
  }
452
0
}
453
454
void TelnetAppender::setNonBlocking(bool newValue)
455
0
{
456
0
  _priv->nonBlocking = newValue;
457
0
}
458
459
void TelnetAppender::setReuseAddress(bool reuseAddress)
460
0
{
461
0
  _priv->reuseAddress = reuseAddress;
462
0
}
463
464
bool TelnetAppender::requiresLayout() const
465
0
{
466
0
  return false;
467
0
}