Coverage Report

Created: 2023-09-25 07:09

/src/usbguard/src/Library/public/usbguard/Logger.cpp
Line
Count
Source (jump to first uncovered line)
1
//
2
// Copyright (C) 2016 Red Hat, Inc.
3
//
4
// This program is free software; you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation; either version 2 of the License, or
7
// (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17
// Authors: Daniel Kopecek <dkopecek@redhat.com>
18
//
19
#ifdef HAVE_BUILD_CONFIG_H
20
  #include <build-config.h>
21
#endif
22
23
#include "Common/Utility.hpp"
24
25
#include "usbguard/Logger.hpp"
26
#include "usbguard/Exception.hpp"
27
28
#include <iostream>
29
#include <fstream>
30
#include <stdio.h>
31
#include <sys/types.h>
32
#include <sys/stat.h>
33
#include <unistd.h>
34
#include <string.h>
35
#include <sys/time.h>
36
#include <syslog.h>
37
#ifndef __STDC_FORMAT_MACROS
38
  #define __STDC_FORMAT_MACROS
39
#endif
40
#include <inttypes.h>
41
42
namespace usbguard
43
{
44
  /* Instantiate the logger */
45
  Logger G_logger;
46
47
  const std::string LogStream::sourceToString(const Source& source)
48
0
  {
49
0
    return source.file + "@" + std::to_string(source.line) + "/" + source.function;
50
0
  }
51
52
  const std::string LogStream::levelToString(Level level)
53
0
  {
54
0
    switch (level) {
55
0
    case LogStream::Level::Audit:
56
0
      return "(A)";
57
58
0
    case LogStream::Level::Error:
59
0
      return "(E)";
60
61
0
    case LogStream::Level::Warning:
62
0
      return "(W)";
63
64
0
    case LogStream::Level::Info:
65
0
      return "(i)";
66
67
0
    case LogStream::Level::Debug:
68
0
      return "(D)";
69
70
0
    case LogStream::Level::Trace:
71
0
      return "(T)";
72
73
0
    default:
74
0
      throw std::runtime_error("BUG: unknown LogStream level value");
75
0
    }
76
0
  }
77
78
  LogStream::LogStream(Logger& logger, const Source& source, const Level level)
79
    : _logger(logger),
80
      _source(source),
81
      _level(level)
82
0
  {
83
0
  }
Unexecuted instantiation: usbguard::LogStream::LogStream(usbguard::Logger&, usbguard::LogStream::Source const&, usbguard::LogStream::Level)
Unexecuted instantiation: usbguard::LogStream::LogStream(usbguard::Logger&, usbguard::LogStream::Source const&, usbguard::LogStream::Level)
84
85
  LogStream::LogStream(const LogStream& rhs)
86
    : std::basic_ios<std::ostringstream::char_type, std::ostringstream::traits_type>(),
87
      std::ostringstream(rhs.str()),
88
      _logger(rhs._logger),
89
      _source(rhs._source),
90
      _level(rhs._level)
91
0
  {
92
0
  }
Unexecuted instantiation: usbguard::LogStream::LogStream(usbguard::LogStream const&)
Unexecuted instantiation: usbguard::LogStream::LogStream(usbguard::LogStream const&)
93
94
  LogStream::~LogStream()
95
0
  {
96
0
    _logger.write(_source, _level, str());
97
0
  }
98
99
  LogSink::LogSink(const std::string& name)
100
    : _name(name)
101
2
  {
102
2
  }
103
104
  LogSink::~LogSink()
105
0
  {
106
0
  }
107
108
  const std::string& LogSink::name() const
109
2
  {
110
2
    return _name;
111
2
  }
112
113
  /*
114
   * Internally required sinks
115
   */
116
  class OStreamSink : public LogSink
117
  {
118
  public:
119
    OStreamSink(const std::string& name, std::ostream& stream)
120
      : LogSink(name),
121
        _ostream(stream)
122
2
    {
123
2
    }
124
125
    void write(const LogStream::Source& source, LogStream::Level level, const std::string& message)
126
0
    {
127
0
      _ostream << '[' << Logger::timestamp() << "] ";
128
0
      _ostream << LogStream::levelToString(level) << " ";
129
130
0
      if (level >= LogStream::Level::Debug) {
131
0
        _ostream << LogStream::sourceToString(source) << ": ";
132
0
      }
133
134
0
      _ostream << message;
135
0
      _ostream << std::endl;
136
0
    }
137
138
    ~OStreamSink()
139
0
    {
140
0
      _ostream.flush();
141
0
    }
142
  private:
143
    std::ostream& _ostream;
144
  };
145
146
  class ConsoleSink : public OStreamSink
147
  {
148
  public:
149
    ConsoleSink()
150
      : OStreamSink("console", std::clog)
151
2
    {
152
2
    }
153
  };
154
155
  class SyslogSink : public LogSink
156
  {
157
  public:
158
    SyslogSink(const std::string& ident)
159
      : LogSink("syslog"),
160
        _ident(ident)
161
0
    {
162
0
      openlog(_ident.c_str(), LOG_NDELAY|LOG_PID|LOG_CONS, LOG_DAEMON);
163
0
    }
164
165
    ~SyslogSink()
166
0
    {
167
0
      closelog();
168
0
    }
169
170
    int levelToPriority(const LogStream::Level level)
171
0
    {
172
0
      switch (level) {
173
0
      case LogStream::Level::Audit:
174
0
        return LOG_NOTICE;
175
176
0
      case LogStream::Level::Error:
177
0
        return LOG_ERR;
178
179
0
      case LogStream::Level::Warning:
180
0
        return LOG_WARNING;
181
182
0
      case LogStream::Level::Info:
183
0
        return LOG_INFO;
184
185
0
      case LogStream::Level::Debug:
186
0
      case LogStream::Level::Trace:
187
0
        return LOG_DEBUG;
188
189
0
      default:
190
0
        throw USBGUARD_BUG("Invalid LogStream::Level value");
191
0
      }
192
0
    }
193
194
    void write(const LogStream::Source& source, LogStream::Level level, const std::string& message)
195
0
    {
196
0
      std::string log_message;
197
198
0
      if (level >= LogStream::Level::Debug) {
199
0
        log_message.append(LogStream::sourceToString(source));
200
0
        log_message.append(": ");
201
0
      }
202
203
0
      log_message.append(message);
204
0
      syslog(levelToPriority(level), "%s", log_message.c_str());
205
0
    }
206
207
  private:
208
    std::string _ident;
209
  };
210
211
  class FileSink : public OStreamSink
212
  {
213
  public:
214
    FileSink(const std::string& filepath, bool append = true)
215
      : OStreamSink("file", _stream)
216
0
    {
217
0
      _filepath = filepath;
218
219
0
      try {
220
0
        _stream.exceptions(std::fstream::failbit);
221
0
        _stream.open(filepath, append ? std::fstream::app : std::fstream::trunc);
222
0
      }
223
0
      catch (...) {
224
0
        throw Exception("FileSink", filepath, "failed to open");
225
0
      }
226
0
    }
227
228
    ~FileSink()
229
0
    {
230
0
      _stream.close();
231
0
    }
232
  private:
233
    std::string _filepath;
234
    std::ofstream _stream;
235
  };
236
237
  class AuditFileSink : public OStreamSink
238
  {
239
  public:
240
    AuditFileSink(const std::string& filepath)
241
      : OStreamSink("auditfile", _stream)
242
0
    {
243
0
      _filepath = filepath;
244
0
      const auto saved_umask = umask(0177);
245
246
0
      try {
247
0
        _stream.exceptions(std::fstream::failbit);
248
0
        _stream.open(filepath, std::fstream::app);
249
0
      }
250
0
      catch (...) {
251
0
        umask(saved_umask);
252
0
        throw Exception("AuditFileSink", filepath, "failed to open");
253
0
      }
254
255
0
      umask(saved_umask);
256
0
    }
257
258
    void write(const LogStream::Source& source, LogStream::Level level, const std::string& message)
259
0
    {
260
      /*
261
       * AuditFileSink logs only Audit level messages.
262
       */
263
0
      if (level == LogStream::Level::Audit) {
264
0
        OStreamSink::write(source, level, message);
265
0
      }
266
0
    }
267
268
    ~AuditFileSink()
269
0
    {
270
0
      _stream.close();
271
0
    }
272
  private:
273
    std::string _filepath;
274
    std::ofstream _stream;
275
  };
276
277
  Logger::Logger()
278
    : _enabled(true),
279
      _level(LogStream::Level::Warning)
280
2
  {
281
2
    const char* const envval = getenv("USBGUARD_DEBUG");
282
283
    /*
284
     * If USBGUARD_DEBUG=1 is set in the current environment,
285
     * set the debugging level to the highest level.
286
     */
287
2
    if (envval != nullptr && strcmp(envval, "1") == 0) {
288
0
      _level = LogStream::Level::Trace;
289
0
    }
290
291
2
    setOutputConsole(true);
292
2
  }
293
294
  Logger::~Logger()
295
0
  {
296
0
  }
297
298
  std::unique_lock<std::mutex> Logger::lock() const
299
128k
  {
300
128k
    return std::unique_lock<std::mutex>(_mutex);
301
128k
  }
302
303
  void Logger::setEnabled(bool state, LogStream::Level level)
304
0
  {
305
0
    auto L = lock();
306
0
    _enabled = state;
307
0
    _level = level;
308
0
  }
309
310
  bool Logger::isEnabled(LogStream::Level level) const
311
128k
  {
312
128k
    auto L = lock();
313
128k
    return (_enabled && _level >= level);
314
128k
  }
315
316
  void Logger::setOutputConsole(const bool state)
317
2
  {
318
2
    auto L = lock();
319
320
2
    if (state == true) {
321
2
      std::unique_ptr<LogSink> sink(new ConsoleSink);
322
2
      addOutputSink_nolock(sink);
323
2
    }
324
0
    else {
325
0
      delOutputSink_nolock("console");
326
0
    }
327
2
  }
328
329
  void Logger::setOutputFile(bool state, const std::string& filepath, bool append)
330
0
  {
331
0
    auto L = lock();
332
333
0
    if (state == true) {
334
0
      std::unique_ptr<LogSink> sink(new FileSink(filepath, append));
335
0
      addOutputSink_nolock(sink);
336
0
    }
337
0
    else {
338
0
      delOutputSink_nolock("file");
339
0
    }
340
0
  }
341
342
  void Logger::setOutputSyslog(bool state, const std::string& ident)
343
0
  {
344
0
    auto L = lock();
345
346
0
    if (state == true) {
347
0
      std::unique_ptr<LogSink> sink(new SyslogSink(ident));
348
0
      addOutputSink_nolock(sink);
349
0
    }
350
0
    else {
351
0
      delOutputSink_nolock("syslog");
352
0
    }
353
0
  }
354
355
  void Logger::setAuditFile(bool state, const std::string& filepath)
356
0
  {
357
0
    auto L = lock();
358
359
0
    if (state == true) {
360
0
      std::unique_ptr<LogSink> sink(new AuditFileSink(filepath));
361
0
      addOutputSink_nolock(sink);
362
0
    }
363
0
    else {
364
0
      delOutputSink_nolock("auditfile");
365
0
    }
366
0
  }
367
368
  void Logger::addOutputSink(std::unique_ptr<LogSink>& sink)
369
0
  {
370
0
    auto L = lock();
371
0
    addOutputSink_nolock(sink);
372
0
  }
373
374
  void Logger::addOutputSink_nolock(std::unique_ptr<LogSink>& sink)
375
2
  {
376
2
    _sinks.emplace(sink->name(), std::move(sink));
377
2
  }
378
379
  void Logger::delOutputSink(const std::string& name)
380
0
  {
381
0
    auto L = lock();
382
0
    delOutputSink_nolock(name);
383
0
  }
384
385
  void Logger::delOutputSink_nolock(const std::string& name)
386
0
  {
387
0
    _sinks.erase(name);
388
0
  }
389
390
  LogStream Logger::operator()(const std::string& file, const int line, const std::string& function, LogStream::Level level)
391
0
  {
392
0
    const LogStream::Source source = {
393
0
      filenameFromPath(file, /*include_extension=*/true), line, function
394
0
    };
395
0
    return LogStream(*this, source, level);
396
0
  }
397
398
  void Logger::write(const LogStream::Source& source, const LogStream::Level level, const std::string& message)
399
0
  {
400
0
    auto L = lock();
401
402
0
    for (auto& kv_pair : _sinks) {
403
0
      auto& sink = kv_pair.second;
404
405
0
      try {
406
0
        sink->write(source, level, message);
407
0
      }
408
0
      catch (const std::exception& ex) {
409
0
        std::cerr << "Warning: sink->write failed for " << sink->name() << " sink: " << ex.what() << std::endl;
410
0
      }
411
0
    }
412
0
  }
413
414
  /*
415
   * Generate a timestamp string in the form:
416
   * <seconds>.<microseconds>
417
   */
418
  const std::string Logger::timestamp()
419
0
  {
420
0
    struct timeval tv_now = { 0, 0 };
421
422
0
    if (gettimeofday(&tv_now, nullptr) != 0) {
423
0
      throw std::runtime_error("gettimeofday");
424
0
    }
425
426
    /*
427
     * The following piece of code should work fine until
428
     * Sat Nov 20 17:46:39 UTC 2286.
429
     */
430
0
    char buffer[16];
431
0
    const int length = snprintf(buffer, sizeof buffer, "%.10" PRIu64 ".%03" PRIu64,
432
0
        (uint64_t)tv_now.tv_sec,
433
0
        (uint64_t)(tv_now.tv_usec / 1000));
434
435
0
    if (length < 1 || static_cast<size_t>(length) > (sizeof buffer - 1)) {
436
0
      throw std::runtime_error("Failed to convert timestamp to string");
437
0
    }
438
439
0
    return std::string(buffer, (size_t)length);
440
0
  }
441
} /* namespace usbguard */
442
443
/* vim: set ts=2 sw=2 et */