1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/syslog_logging.h"
6
7 #include "build/build_config.h"
8
9 #if BUILDFLAG(IS_WIN)
10 // clang-format off
11 #include <windows.h> // Must be in front of other Windows header files.
12 // clang-format on
13
14 #include <sddl.h>
15
16 #include "base/debug/stack_trace.h"
17 #include "base/strings/string_util.h"
18 #include "base/win/scoped_handle.h"
19 #include "base/win/win_util.h"
20 #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
21 // <syslog.h> defines LOG_INFO, LOG_WARNING macros that could conflict with
22 // base::LOG_INFO, base::LOG_WARNING.
23 #include <syslog.h>
24 #undef LOG_INFO
25 #undef LOG_WARNING
26 #endif
27
28 #include <ostream>
29 #include <string>
30
31 namespace logging {
32
33 namespace {
34
35 // The syslog logging is on by default, but tests or fuzzers can disable it.
36 bool g_logging_enabled = true;
37
38 } // namespace
39
40 #if BUILDFLAG(IS_WIN)
41
42 namespace {
43
44 std::string* g_event_source_name = nullptr;
45 uint16_t g_category = 0;
46 uint32_t g_event_id = 0;
47 std::wstring* g_user_sid = nullptr;
48
49 class EventLogHandleTraits {
50 public:
51 using Handle = HANDLE;
52
53 EventLogHandleTraits() = delete;
54 EventLogHandleTraits(const EventLogHandleTraits&) = delete;
55 EventLogHandleTraits& operator=(const EventLogHandleTraits&) = delete;
56
57 // Closes the handle.
CloseHandle(HANDLE handle)58 static bool CloseHandle(HANDLE handle) {
59 return ::DeregisterEventSource(handle) != FALSE;
60 }
61
62 // Returns true if the handle value is valid.
IsHandleValid(HANDLE handle)63 static bool IsHandleValid(HANDLE handle) { return handle != nullptr; }
64
65 // Returns null handle value.
NullHandle()66 static HANDLE NullHandle() { return nullptr; }
67 };
68
69 using ScopedEventLogHandle =
70 base::win::GenericScopedHandle<EventLogHandleTraits,
71 base::win::DummyVerifierTraits>;
72
73 } // namespace
74
SetEventSource(const std::string & name,uint16_t category,uint32_t event_id)75 void SetEventSource(const std::string& name,
76 uint16_t category,
77 uint32_t event_id) {
78 DCHECK_EQ(nullptr, g_event_source_name);
79 g_event_source_name = new std::string(name);
80 g_category = category;
81 g_event_id = event_id;
82 DCHECK_EQ(nullptr, g_user_sid);
83 g_user_sid = new std::wstring();
84 base::win::GetUserSidString(g_user_sid);
85 }
86
ResetEventSourceForTesting()87 void ResetEventSourceForTesting() {
88 delete g_event_source_name;
89 g_event_source_name = nullptr;
90 delete g_user_sid;
91 g_user_sid = nullptr;
92 }
93
94 #endif // BUILDFLAG(IS_WIN)
95
EventLogMessage(const char * file,int line,LogSeverity severity)96 EventLogMessage::EventLogMessage(const char* file,
97 int line,
98 LogSeverity severity)
99 : log_message_(file, line, severity) {
100 }
101
~EventLogMessage()102 EventLogMessage::~EventLogMessage() {
103 if (!g_logging_enabled)
104 return;
105
106 #if BUILDFLAG(IS_WIN)
107 // If g_event_source_name is nullptr (which it is per default) SYSLOG will
108 // degrade gracefully to regular LOG. If you see this happening most probably
109 // you are using SYSLOG before you called SetEventSourceName.
110 if (g_event_source_name == nullptr)
111 return;
112
113 ScopedEventLogHandle event_log_handle(
114 RegisterEventSourceA(nullptr, g_event_source_name->c_str()));
115
116 if (!event_log_handle.is_valid()) {
117 stream() << " !!NOT ADDED TO EVENTLOG!!";
118 return;
119 }
120
121 std::string message(log_message_.str());
122 WORD log_type = EVENTLOG_ERROR_TYPE;
123 switch (log_message_.severity()) {
124 case LOGGING_INFO:
125 log_type = EVENTLOG_INFORMATION_TYPE;
126 break;
127 case LOGGING_WARNING:
128 log_type = EVENTLOG_WARNING_TYPE;
129 break;
130 case LOGGING_ERROR:
131 case LOGGING_FATAL:
132 // The price of getting the stack trace is not worth the hassle for
133 // non-error conditions.
134 base::debug::StackTrace trace;
135 message.append(trace.ToString());
136 log_type = EVENTLOG_ERROR_TYPE;
137 break;
138 }
139 LPCSTR strings[1] = {message.data()};
140 PSID user_sid = nullptr;
141 if (!::ConvertStringSidToSid(g_user_sid->c_str(), &user_sid)) {
142 stream() << " !!ERROR GETTING USER SID!!";
143 }
144
145 if (!ReportEventA(event_log_handle.get(), log_type, g_category, g_event_id,
146 user_sid, 1, 0, strings, nullptr)) {
147 stream() << " !!NOT ADDED TO EVENTLOG!!";
148 }
149
150 if (user_sid != nullptr)
151 ::LocalFree(user_sid);
152 #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
153 const char kEventSource[] = "chrome";
154 openlog(kEventSource, LOG_NOWAIT | LOG_PID, LOG_USER);
155 // We can't use the defined names for the logging severity from syslog.h
156 // because they collide with the names of our own severity levels. Therefore
157 // we use the actual values which of course do not match ours.
158 // See sys/syslog.h for reference.
159 int priority = 3;
160 switch (log_message_.severity()) {
161 case LOGGING_INFO:
162 priority = 6;
163 break;
164 case LOGGING_WARNING:
165 priority = 4;
166 break;
167 case LOGGING_ERROR:
168 priority = 3;
169 break;
170 case LOGGING_FATAL:
171 priority = 2;
172 break;
173 }
174 syslog(priority, "%s", log_message_.str().c_str());
175 closelog();
176 #endif // BUILDFLAG(IS_WIN)
177 }
178
SetSyslogLoggingForTesting(bool logging_enabled)179 void SetSyslogLoggingForTesting(bool logging_enabled) {
180 g_logging_enabled = logging_enabled;
181 }
182
183 } // namespace logging
184