Coverage Report

Created: 2025-07-01 06:08

/src/logging-log4cxx/src/main/cpp/telnetappender.cpp
Line
Count
Source (jump to first uncovered line)
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
#include <mutex>
28
#include <thread>
29
#include <vector>
30
31
#if LOG4CXX_EVENTS_AT_EXIT
32
#include <log4cxx/private/atexitregistry.h>
33
#endif
34
35
using namespace LOG4CXX_NS;
36
using namespace LOG4CXX_NS::helpers;
37
using namespace LOG4CXX_NS::net;
38
39
typedef helpers::SocketPtr Connection;
40
LOG4CXX_LIST_DEF(ConnectionList, Connection);
41
42
IMPLEMENT_LOG4CXX_OBJECT(TelnetAppender)
43
44
struct TelnetAppender::TelnetAppenderPriv : public AppenderSkeletonPrivate
45
{
46
0
  TelnetAppenderPriv( int port, int maxConnections ) : AppenderSkeletonPrivate(),
47
0
    port(port),
48
0
    connections(maxConnections),
49
0
    encoding(LOG4CXX_STR("UTF-8")),
50
0
    encoder(CharsetEncoder::getUTF8Encoder()),
51
0
    activeConnections(0)
52
#if LOG4CXX_EVENTS_AT_EXIT
53
    , atExitRegistryRaii([this]{stopAcceptingConnections();})
54
#endif
55
0
  { }
56
57
  ~TelnetAppenderPriv()
58
0
  { stopAcceptingConnections(); }
59
60
  int port;
61
  LogString hostname;
62
  bool reuseAddress = false;
63
  ConnectionList connections;
64
  LogString encoding;
65
  LOG4CXX_NS::helpers::CharsetEncoderPtr encoder;
66
  std::unique_ptr<helpers::ServerSocket> serverSocket;
67
  std::thread sh;
68
  size_t activeConnections;
69
70
#if LOG4CXX_EVENTS_AT_EXIT
71
  helpers::AtExitRegistry::Raii atExitRegistryRaii;
72
#endif
73
74
  void stopAcceptingConnections()
75
0
  {
76
0
    {
77
0
      std::lock_guard<std::recursive_mutex> lock(this->mutex);
78
0
      if (!this->serverSocket || this->closed)
79
0
        return;
80
0
      this->closed = true;
81
0
    }
82
    // Interrupt accept()
83
0
    try
84
0
    {
85
0
      this->serverSocket->close();
86
0
    }
87
0
    catch (Exception&)
88
0
    {
89
0
    }
90
0
    if (this->sh.joinable())
91
0
      this->sh.join();
92
0
  }
93
};
94
95
0
#define _priv static_cast<TelnetAppenderPriv*>(m_priv.get())
96
97
/** The default telnet server port */
98
const int TelnetAppender::DEFAULT_PORT = 23;
99
100
/** The maximum number of concurrent connections */
101
const int TelnetAppender::MAX_CONNECTIONS = 20;
102
103
TelnetAppender::TelnetAppender()
104
0
  : AppenderSkeleton (std::make_unique<TelnetAppenderPriv>(DEFAULT_PORT, MAX_CONNECTIONS))
105
0
{
106
0
}
Unexecuted instantiation: log4cxx::net::TelnetAppender::TelnetAppender()
Unexecuted instantiation: log4cxx::net::TelnetAppender::TelnetAppender()
107
108
TelnetAppender::~TelnetAppender()
109
0
{
110
0
  finalize();
111
0
}
112
113
void TelnetAppender::activateOptions(Pool& /* p */)
114
0
{
115
0
  if (_priv->serverSocket == NULL)
116
0
  {
117
0
    _priv->serverSocket = ServerSocket::create(_priv->port, _priv->reuseAddress, _priv->hostname);
118
0
    _priv->serverSocket->setSoTimeout(1000);
119
0
  }
120
121
0
  if (!_priv->sh.joinable())
122
0
    _priv->sh = ThreadUtility::instance()->createThread( LOG4CXX_STR("TelnetAppender"), &TelnetAppender::acceptConnections, this );
123
0
}
124
125
void TelnetAppender::setOption(const LogString& option,
126
  const LogString& value)
127
0
{
128
0
  if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("PORT"), LOG4CXX_STR("port")))
129
0
  {
130
0
    setPort(OptionConverter::toInt(value, DEFAULT_PORT));
131
0
  }
132
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("MAXCONNECTIONS"), LOG4CXX_STR("maxconnections")))
133
0
  {
134
0
    setMaxConnections(OptionConverter::toInt(value, MAX_CONNECTIONS));
135
0
  }
136
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("ENCODING"), LOG4CXX_STR("encoding")))
137
0
  {
138
0
    setEncoding(value);
139
0
  }
140
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("REUSEADDRESS"), LOG4CXX_STR("reuseaddress")))
141
0
  {
142
0
    setReuseAddress(OptionConverter::toBoolean(value, true));
143
0
  }
144
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("HOSTNAME"), LOG4CXX_STR("hostname")))
145
0
  {
146
0
    setHostname(value);
147
0
  }
148
0
  else
149
0
  {
150
0
    AppenderSkeleton::setOption(option, value);
151
0
  }
152
0
}
153
154
LogString TelnetAppender::getEncoding() const
155
0
{
156
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
157
0
  return _priv->encoding;
158
0
}
159
160
void TelnetAppender::setEncoding(const LogString& value)
161
0
{
162
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
163
0
  _priv->encoder = CharsetEncoder::getEncoder(value);
164
0
  _priv->encoding = value;
165
0
}
166
167
168
void TelnetAppender::close()
169
0
{
170
0
  _priv->stopAcceptingConnections();
171
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
172
0
  SocketPtr nullSocket;
173
0
  for (auto& item : _priv->connections)
174
0
  {
175
0
    if (item)
176
0
    {
177
0
      item->close();
178
0
      item = nullSocket;
179
0
    }
180
0
  }
181
0
  _priv->activeConnections = 0;
182
0
}
183
184
185
void TelnetAppender::write(ByteBuffer& buf)
186
0
{
187
0
  for (auto& item :_priv->connections)
188
0
  {
189
0
    if (item)
190
0
    {
191
0
      try
192
0
      {
193
0
        ByteBuffer b(buf.current(), buf.remaining());
194
0
        item->write(b);
195
0
      }
196
0
      catch (Exception&)
197
0
      {
198
        // The client has closed the connection, remove it from our list:
199
0
        item.reset();
200
0
        _priv->activeConnections--;
201
0
      }
202
0
    }
203
0
  }
204
0
}
205
206
void TelnetAppender::writeStatus(const SocketPtr& socket, const LogString& msg, Pool& p)
207
0
{
208
0
  size_t bytesSize = msg.size() * 2;
209
0
  char* bytes = p.pstralloc(bytesSize);
210
211
0
  LogString::const_iterator msgIter(msg.begin());
212
0
  ByteBuffer buf(bytes, bytesSize);
213
214
0
  while (msgIter != msg.end())
215
0
  {
216
0
    _priv->encoder->encode(msg, msgIter, buf);
217
0
    buf.flip();
218
0
    socket->write(buf);
219
0
    buf.clear();
220
0
  }
221
0
}
222
223
void TelnetAppender::append(const spi::LoggingEventPtr& event, Pool& p)
224
0
{
225
0
  size_t count = _priv->activeConnections;
226
227
0
  if (count > 0)
228
0
  {
229
0
    LogString msg;
230
0
    if (_priv->layout)
231
0
      _priv->layout->format(msg, event, p);
232
0
    else
233
0
      msg = event->getMessage();
234
0
    msg.append(LOG4CXX_STR("\r\n"));
235
0
    size_t bytesSize = msg.size() * 2;
236
0
    char* bytes = p.pstralloc(bytesSize);
237
238
0
    LogString::const_iterator msgIter(msg.begin());
239
0
    ByteBuffer buf(bytes, bytesSize);
240
241
0
    std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
242
243
0
    while (msgIter != msg.end())
244
0
    {
245
0
      log4cxx_status_t stat = _priv->encoder->encode(msg, msgIter, buf);
246
0
      buf.flip();
247
0
      write(buf);
248
0
      buf.clear();
249
250
0
      if (CharsetEncoder::isError(stat))
251
0
      {
252
0
        LogString unrepresented(1, 0x3F /* '?' */);
253
0
        LogString::const_iterator unrepresentedIter(unrepresented.begin());
254
0
        stat = _priv->encoder->encode(unrepresented, unrepresentedIter, buf);
255
0
        buf.flip();
256
0
        write(buf);
257
0
        buf.clear();
258
0
        msgIter++;
259
0
      }
260
0
    }
261
0
  }
262
0
}
263
264
void TelnetAppender::acceptConnections()
265
0
{
266
267
  // main loop; is left when This->closed is != 0 after an accept()
268
0
  while (true)
269
0
  {
270
0
    try
271
0
    {
272
0
      SocketPtr newClient = _priv->serverSocket->accept();
273
0
      bool done = _priv->closed;
274
275
0
      if (done)
276
0
      {
277
0
        Pool p;
278
0
        writeStatus(newClient, LOG4CXX_STR("Log closed.\r\n"), p);
279
0
        newClient->close();
280
281
0
        break;
282
0
      }
283
284
0
      size_t count = _priv->activeConnections;
285
286
0
      if (count >= _priv->connections.size())
287
0
      {
288
0
        Pool p;
289
0
        writeStatus(newClient, LOG4CXX_STR("Too many connections.\r\n"), p);
290
0
        newClient->close();
291
0
      }
292
0
      else
293
0
      {
294
        //
295
        //   find unoccupied connection
296
        //
297
0
        std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
298
299
0
        for (auto& item : _priv->connections)
300
0
        {
301
0
          if (!item)
302
0
          {
303
0
            item = newClient;
304
0
            _priv->activeConnections++;
305
306
0
            break;
307
0
          }
308
0
        }
309
310
0
        Pool p;
311
0
        LogString oss(LOG4CXX_STR("TelnetAppender v1.0 ("));
312
0
        StringHelper::toString((int) count + 1, p, oss);
313
0
        oss += LOG4CXX_STR(" active connections)\r\n\r\n");
314
0
        writeStatus(newClient, oss, p);
315
0
      }
316
0
    }
317
0
    catch (InterruptedIOException&)
318
0
    {
319
0
      if (_priv->closed)
320
0
      {
321
0
        break;
322
0
      }
323
0
    }
324
0
    catch (Exception& e)
325
0
    {
326
0
      if (!_priv->closed)
327
0
      {
328
0
        LogLog::error(LOG4CXX_STR("Encountered error while in SocketHandler loop."), e);
329
0
      }
330
0
      else
331
0
      {
332
0
        break;
333
0
      }
334
0
    }
335
0
  }
336
337
0
}
338
339
int TelnetAppender::getPort() const
340
0
{
341
0
  return _priv->port;
342
0
}
343
344
void TelnetAppender::setPort(int port1)
345
0
{
346
0
  _priv->port = port1;
347
0
}
348
349
LogString TelnetAppender::getHostname() const
350
0
{
351
0
  return _priv->hostname;
352
0
}
353
354
void TelnetAppender::setHostname(const LogString& hostname)
355
0
{
356
0
  _priv->hostname = hostname;
357
0
}
358
359
int TelnetAppender::getMaxConnections() const
360
0
{
361
0
  return static_cast<int>(_priv->connections.size());
362
0
}
363
364
void TelnetAppender::setMaxConnections(int newValue)
365
0
{
366
0
  std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
367
0
  if (_priv->connections.size() < newValue)
368
0
    _priv->connections.resize(newValue);
369
0
  else while (newValue < _priv->connections.size())
370
0
  {
371
0
    auto item = _priv->connections.back();
372
0
    _priv->connections.pop_back();
373
0
    if (item)
374
0
    {
375
0
      item->close();
376
0
      --_priv->activeConnections;
377
0
    }
378
0
  }
379
0
}
380
381
void TelnetAppender::setReuseAddress(bool reuseAddress)
382
0
{
383
0
  _priv->reuseAddress = reuseAddress;
384
0
}
385
386
bool TelnetAppender::requiresLayout() const
387
0
{
388
0
  return false;
389
0
}