| OLD | NEW |
| (Empty) |
| 1 // Copyright 2003-2009 Google Inc. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 // ======================================================================== | |
| 15 // | |
| 16 // Tracing and logging system. | |
| 17 // | |
| 18 // The log output goes to "All Users/Application Data/Google/Update/Log/". | |
| 19 // The log configuration file is under | |
| 20 // "%Program Files%/C:\Program Files\Google\Common\Update". | |
| 21 // By default, the logging is on in debug modes and off in opt mode although in | |
| 22 // opt mode only the OPT_LOG statements write to the log. | |
| 23 // | |
| 24 // The log is open to all the users to write to. There is a known vulnerability | |
| 25 // where a DOS can be created by holding on to the logging mutex. | |
| 26 // | |
| 27 // In this module use of ASSERT & REPORT is banned. This is to prevent any | |
| 28 // possible recursion issues between logging (logging.h) and | |
| 29 // asserting/reporting (debug.h). Both are basement-level systems that need to | |
| 30 // work when almost nothing else works and interdependencies are best avoided. | |
| 31 // One unavoidable interdependency is that debugASSERT will send messages to | |
| 32 // the logger via Logger::OutputMessage - which then broadcasts it to each | |
| 33 // LogWriter's OutputMessage. So these methods should be as simple as | |
| 34 // possible. (Also, unlike asserting/reporting - this module will not avoid | |
| 35 // use of the heap, however the code executed from Logger::OutputMessage | |
| 36 // doesn't use the heap (for all the LogWriters in this file).) | |
| 37 // | |
| 38 // TODO(omaha): implement the minidump handling in terms of breakpad. | |
| 39 // Log initialization if full of lazy init. Consider doing | |
| 40 // eager init of log and its registered log writers when the | |
| 41 // log is created and initialized. | |
| 42 // Reimplement in terms of smart handles and locks. | |
| 43 // Reimplement without dependency on any other compilation unit | |
| 44 // that can call assert, verify, or the log itself. | |
| 45 // Redo the history logging feature | |
| 46 | |
| 47 #include "omaha/base/logging.h" | |
| 48 | |
| 49 #include <excpt.h> // Microsoft specific: structured exceptions. | |
| 50 #include <shlobj.h> | |
| 51 #include <shlwapi.h> | |
| 52 #include <string.h> | |
| 53 #include <atlpath.h> | |
| 54 #include <atlsecurity.h> | |
| 55 #include "base/basictypes.h" | |
| 56 #include "omaha/base/app_util.h" | |
| 57 #include "omaha/base/const_debug.h" | |
| 58 #include "omaha/base/constants.h" | |
| 59 #include "omaha/base/debug.h" | |
| 60 #include "omaha/base/etw_log_writer.h" | |
| 61 #include "omaha/base/file.h" | |
| 62 #include "omaha/base/path.h" | |
| 63 #include "omaha/base/string.h" | |
| 64 #include "omaha/base/time.h" | |
| 65 #include "omaha/base/utils.h" | |
| 66 | |
| 67 namespace omaha { | |
| 68 | |
| 69 // enforce ban on ASSERT/REPORT | |
| 70 #undef ASSERT | |
| 71 #undef REPORT | |
| 72 | |
| 73 #ifdef LOGGING | |
| 74 | |
| 75 #define kNumLockRetries (20) | |
| 76 #define kLockRetryDelayMs (50) | |
| 77 | |
| 78 // Circular buffer to log history. | |
| 79 static wchar_t history_buffer[kMaxHistoryBufferSize]; | |
| 80 | |
| 81 // Index into the history buffer to begin writing at. | |
| 82 static int history_buffer_next_idx = 0; | |
| 83 | |
| 84 // Indicates whether the history buffer has ever reached its full capacity. | |
| 85 // Once this boolean is true, if never becomes false. | |
| 86 static bool history_buffer_full = false; | |
| 87 | |
| 88 // | |
| 89 // Table of category names to categories. | |
| 90 // | |
| 91 #define LC_ENTRY(lc_value) (L#lc_value), (lc_value) | |
| 92 struct { | |
| 93 wchar_t* category_name; | |
| 94 LogCategory category; | |
| 95 } static LogCategoryNames[] = { | |
| 96 LC_ENTRY(LC_UTIL), | |
| 97 LC_ENTRY(LC_SETUP), | |
| 98 LC_ENTRY(LC_SHELL), | |
| 99 LC_ENTRY(LC_CORE), | |
| 100 LC_ENTRY(LC_JS), | |
| 101 LC_ENTRY(LC_PLUGIN), | |
| 102 LC_ENTRY(LC_SERVICE), | |
| 103 LC_ENTRY(LC_OPT), | |
| 104 LC_ENTRY(LC_NET), | |
| 105 LC_ENTRY(LC_REPORT), | |
| 106 }; | |
| 107 | |
| 108 COMPILE_ASSERT(arraysize(LogCategoryNames) == LC_MAX_CAT - 1, | |
| 109 LogCategoryNames_missing_category); | |
| 110 | |
| 111 static CString StripBackslashes(const CString& name) { | |
| 112 int n = String_FindChar(name, L'\\'); | |
| 113 if (n == -1) { | |
| 114 return name; | |
| 115 } else { | |
| 116 CString result; | |
| 117 for (int i = 0; i < name.GetLength(); ++i) { | |
| 118 if (name[i] != L'\\') { | |
| 119 result += name[i]; | |
| 120 } | |
| 121 } | |
| 122 return result; | |
| 123 } | |
| 124 } | |
| 125 | |
| 126 static CString GetProcName() { | |
| 127 CString proc_name(app_util::GetAppNameWithoutExtension()); | |
| 128 CString module_name(app_util::GetCurrentModuleNameWithoutExtension()); | |
| 129 CString result(proc_name); | |
| 130 if (module_name.CompareNoCase(proc_name) != 0) { | |
| 131 result += L":"; | |
| 132 result += module_name; | |
| 133 } | |
| 134 return result; | |
| 135 } | |
| 136 | |
| 137 // Formats a line prefix with the current time min:sec:milisec if wanted, | |
| 138 // otherwise just the process:module. | |
| 139 static void FormatLinePrefix(bool show_time, | |
| 140 const wchar_t* proc_name, | |
| 141 CString& result) { | |
| 142 if (show_time) { | |
| 143 SYSTEMTIME system_time = {0}; | |
| 144 GetLocalTime(&system_time); | |
| 145 result.Format(L"[%02d/%02d/%02d %02d:%02d:%02d.%03d]", | |
| 146 system_time.wMonth, system_time.wDay, system_time.wYear % 100, | |
| 147 system_time.wHour, system_time.wMinute, system_time.wSecond, | |
| 148 system_time.wMilliseconds); | |
| 149 } | |
| 150 result.AppendFormat(L"[%s][%u:%u]", | |
| 151 proc_name, | |
| 152 ::GetCurrentProcessId(), | |
| 153 ::GetCurrentThreadId()); | |
| 154 } | |
| 155 | |
| 156 static bool g_logging_valid = false; | |
| 157 static Logging g_logging; | |
| 158 | |
| 159 // Singleton factory for the Logging object. | |
| 160 Logging* GetLogging() { | |
| 161 return g_logging_valid ? &g_logging : NULL; | |
| 162 } | |
| 163 | |
| 164 // Force the logging system to be initialized during elaboration of static | |
| 165 // constructors, while the program is still single-threaded. This global | |
| 166 // static object will cause the static logger inside of GetLogging() to be | |
| 167 // constructed. However, it might not be the first call to GetLogging() - | |
| 168 // another static object in another object file might get constructed first, | |
| 169 // and in its constructor call a logging API. Just as long as it is done when | |
| 170 // the system is single threaded. (Reason: because the Logger object has a | |
| 171 // LLock object which has a Win32 critical section which needs to be | |
| 172 // initialized - only once!) | |
| 173 | |
| 174 | |
| 175 Logging::Logging() | |
| 176 : logging_initialized_(false), | |
| 177 logging_enabled_(true), | |
| 178 force_show_time_(false), | |
| 179 show_time_(true), | |
| 180 log_to_file_(true), | |
| 181 log_to_debug_out_(true), | |
| 182 append_to_file_(true), | |
| 183 logging_shutdown_(false), | |
| 184 num_writers_(0), | |
| 185 file_log_writer_(NULL), | |
| 186 debug_out_writer_(NULL), | |
| 187 etw_log_writer_(NULL), | |
| 188 is_initializing_(false), | |
| 189 log_file_name_(kDefaultLogFileName), | |
| 190 config_file_path_(GetConfigurationFilePath()) { | |
| 191 g_last_category_check_time = 0; | |
| 192 for (int i = 0; i < max_writers; ++i) { | |
| 193 writers_[i] = NULL; | |
| 194 } | |
| 195 proc_name_ = GetProcName(); | |
| 196 g_logging_valid = true; | |
| 197 | |
| 198 // Read initial settings from the config file. | |
| 199 ReadLoggingSettings(); | |
| 200 } | |
| 201 | |
| 202 // TODO(omaha): why aren't we using a mutexscope and what if an the code | |
| 203 // throws? Will the lock be unlocked? | |
| 204 Logging::~Logging() { | |
| 205 // Acquire the lock outside the try/except block so we'll always release it | |
| 206 lock_.Lock(); | |
| 207 | |
| 208 __try { | |
| 209 // prevent further access to the system | |
| 210 // necessary because the static destructors happen | |
| 211 // in a non-deterministic order | |
| 212 logging_shutdown_ = true; | |
| 213 | |
| 214 // Delete all registered LogWriters | |
| 215 for (int i = 0; i < num_writers_; ++i) { | |
| 216 if (writers_[i]) { | |
| 217 delete writers_[i]; | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 logging_initialized_ = false; | |
| 222 } __except(SehNoMinidump(GetExceptionCode(), | |
| 223 GetExceptionInformation(), | |
| 224 __FILE__, | |
| 225 __LINE__, | |
| 226 true)) { | |
| 227 OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n"); | |
| 228 logging_initialized_ = false; | |
| 229 } | |
| 230 | |
| 231 g_logging_valid = false; | |
| 232 lock_.Unlock(); | |
| 233 } | |
| 234 | |
| 235 void Logging::UpdateCatAndLevel(const wchar_t* cat_name, LogCategory cat) { | |
| 236 if (cat_name == NULL) { | |
| 237 return; | |
| 238 } | |
| 239 if (cat >= LC_MAX_CAT) { | |
| 240 return; | |
| 241 } | |
| 242 int log_level = kDefaultLogLevel; | |
| 243 CString config_file = GetCurrentConfigurationFilePath(); | |
| 244 if (!config_file.IsEmpty()) { | |
| 245 log_level = GetPrivateProfileInt(kConfigSectionLoggingLevel, | |
| 246 cat_name, | |
| 247 kDefaultLogLevel, | |
| 248 config_file); | |
| 249 } | |
| 250 category_list_[cat].enabled = (log_level != 0); | |
| 251 category_list_[cat].log_level = static_cast<LogLevel>(log_level); | |
| 252 } | |
| 253 | |
| 254 void Logging::ReadLoggingSettings() { | |
| 255 CString config_file = GetCurrentConfigurationFilePath(); | |
| 256 if (!config_file.IsEmpty()) { | |
| 257 logging_enabled_ = ::GetPrivateProfileInt( | |
| 258 kConfigSectionLoggingSettings, | |
| 259 kConfigAttrEnableLogging, | |
| 260 kDefaultLoggingEnabled, | |
| 261 config_file) == 0 ? false : true; | |
| 262 | |
| 263 show_time_ = ::GetPrivateProfileInt( | |
| 264 kConfigSectionLoggingSettings, | |
| 265 kConfigAttrShowTime, | |
| 266 kDefaultShowTime, | |
| 267 config_file) == 0 ? false : true; | |
| 268 | |
| 269 log_to_file_ = ::GetPrivateProfileInt( | |
| 270 kConfigSectionLoggingSettings, | |
| 271 kConfigAttrLogToFile, | |
| 272 kDefaultLogToFile, | |
| 273 config_file) == 0 ? false : true; | |
| 274 | |
| 275 log_to_debug_out_ = ::GetPrivateProfileInt( | |
| 276 kConfigSectionLoggingSettings, | |
| 277 kConfigAttrLogToOutputDebug, | |
| 278 kDefaultLogToOutputDebug, | |
| 279 config_file) == 0 ? false : true; | |
| 280 | |
| 281 append_to_file_ = ::GetPrivateProfileInt( | |
| 282 kConfigSectionLoggingSettings, | |
| 283 kConfigAttrAppendToFile, | |
| 284 kDefaultAppendToFile, | |
| 285 config_file) == 0 ? false : true; | |
| 286 | |
| 287 ::GetPrivateProfileString(kConfigSectionLoggingSettings, | |
| 288 kConfigAttrLogFilePath, | |
| 289 kDefaultLogFileName, | |
| 290 CStrBuf(log_file_name_, MAX_PATH), | |
| 291 MAX_PATH, | |
| 292 config_file); | |
| 293 } else { | |
| 294 logging_enabled_ = kDefaultLoggingEnabled; | |
| 295 show_time_ = kDefaultShowTime; | |
| 296 log_to_file_ = kDefaultLogToFile; | |
| 297 log_to_debug_out_ = kDefaultLogToOutputDebug; | |
| 298 append_to_file_ = kDefaultAppendToFile; | |
| 299 log_file_name_ = kDefaultLogFileName; | |
| 300 } | |
| 301 | |
| 302 if (force_show_time_) { | |
| 303 show_time_ = true; | |
| 304 } | |
| 305 | |
| 306 // The "default" category is always enabled. | |
| 307 category_list_[LC_LOGGING].enabled = true; | |
| 308 category_list_[LC_LOGGING].log_level = LEVEL_ALL; | |
| 309 | |
| 310 // Read each category from the ini file. | |
| 311 for (size_t i = 0; i < arraysize(LogCategoryNames); ++i) { | |
| 312 UpdateCatAndLevel(LogCategoryNames[i].category_name, | |
| 313 LogCategoryNames[i].category); | |
| 314 } | |
| 315 | |
| 316 g_last_category_check_time = GetCurrent100NSTime(); | |
| 317 } | |
| 318 | |
| 319 CString Logging::GetDefaultLogDirectory() const { | |
| 320 CString path; | |
| 321 CStrBuf buf(path, MAX_PATH); | |
| 322 HRESULT hr = ::SHGetFolderPath(NULL, | |
| 323 CSIDL_COMMON_APPDATA, | |
| 324 NULL, | |
| 325 SHGFP_TYPE_CURRENT, | |
| 326 buf); | |
| 327 if (FAILED(hr)) { | |
| 328 return L""; | |
| 329 } | |
| 330 if (!::PathAppend(buf, OMAHA_REL_LOG_DIR)) { | |
| 331 return L""; | |
| 332 } | |
| 333 return path; | |
| 334 } | |
| 335 | |
| 336 CString Logging::GetLogFilePath() const { | |
| 337 if (log_file_name_.IsEmpty()) { | |
| 338 return CString(); | |
| 339 } | |
| 340 | |
| 341 if (!ATLPath::IsRelative(log_file_name_)) { | |
| 342 return log_file_name_; | |
| 343 } | |
| 344 | |
| 345 CString path = GetDefaultLogDirectory(); | |
| 346 if (path.IsEmpty()) { | |
| 347 return CString(); | |
| 348 } | |
| 349 | |
| 350 if (!::PathAppend(CStrBuf(path, MAX_PATH), log_file_name_)) { | |
| 351 return CString(); | |
| 352 } | |
| 353 | |
| 354 return path; | |
| 355 } | |
| 356 | |
| 357 void Logging::ConfigureETWLogWriter() { | |
| 358 // Always create the ETW log writer, as its log level is controlled | |
| 359 // at runtime through Event Tracing for Windows. | |
| 360 if (etw_log_writer_ == NULL) { | |
| 361 etw_log_writer_ = EtwLogWriter::Create(); | |
| 362 if (debug_out_writer_ == NULL) { | |
| 363 OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - " | |
| 364 L"Cannot create ETW log writer", | |
| 365 proc_name_)); | |
| 366 } | |
| 367 } | |
| 368 | |
| 369 if (etw_log_writer_ != NULL) { | |
| 370 InternalRegisterWriter(etw_log_writer_); | |
| 371 } | |
| 372 } | |
| 373 | |
| 374 void Logging::ConfigureFileLogWriter() { | |
| 375 if (!log_to_file_) { | |
| 376 return; | |
| 377 } | |
| 378 | |
| 379 // Create the logging file. | |
| 380 if (file_log_writer_ == NULL) { | |
| 381 CString path = GetLogFilePath(); | |
| 382 if (path.IsEmpty()) { | |
| 383 return; | |
| 384 } | |
| 385 | |
| 386 // Extract the final target directory which will not be what | |
| 387 // GetDefaultLogDirectory() returns if log_file_name_ is an absolute path. | |
| 388 CString log_file_dir = GetDirectoryFromPath(path); | |
| 389 if (!File::Exists(log_file_dir)) { | |
| 390 if (FAILED(CreateDir(log_file_dir, NULL))) { | |
| 391 return; | |
| 392 } | |
| 393 } | |
| 394 file_log_writer_ = FileLogWriter::Create(path, append_to_file_); | |
| 395 if (file_log_writer_ == NULL) { | |
| 396 OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - " | |
| 397 L"Cannot create log writer to %s", | |
| 398 proc_name_, path)); | |
| 399 } | |
| 400 } | |
| 401 | |
| 402 if (file_log_writer_ != NULL) { | |
| 403 InternalRegisterWriter(file_log_writer_); | |
| 404 } | |
| 405 } | |
| 406 | |
| 407 void Logging::ConfigureDebugOutLogWriter() { | |
| 408 if (!log_to_debug_out_) { | |
| 409 return; | |
| 410 } | |
| 411 | |
| 412 if (debug_out_writer_ == NULL) { | |
| 413 debug_out_writer_ = OutputDebugStringLogWriter::Create(); | |
| 414 if (debug_out_writer_ == NULL) { | |
| 415 OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - " | |
| 416 L"Cannot create OutputDebugString log writer", | |
| 417 proc_name_)); | |
| 418 } | |
| 419 } | |
| 420 | |
| 421 if (debug_out_writer_ != NULL) { | |
| 422 InternalRegisterWriter(debug_out_writer_); | |
| 423 } | |
| 424 } | |
| 425 | |
| 426 // Configures/unconfigures the log writers for the current settings. | |
| 427 bool Logging::ConfigureLogging() { | |
| 428 ConfigureETWLogWriter(); | |
| 429 ConfigureFileLogWriter(); | |
| 430 ConfigureDebugOutLogWriter(); | |
| 431 | |
| 432 return num_writers_ > 0; | |
| 433 } | |
| 434 | |
| 435 void Logging::UnconfigureLogging() { | |
| 436 if (etw_log_writer_ != NULL) { | |
| 437 InternalUnregisterWriter(etw_log_writer_); | |
| 438 } | |
| 439 if (file_log_writer_ != NULL) { | |
| 440 InternalUnregisterWriter(file_log_writer_); | |
| 441 } | |
| 442 if (debug_out_writer_ != NULL) { | |
| 443 InternalUnregisterWriter(debug_out_writer_); | |
| 444 } | |
| 445 } | |
| 446 | |
| 447 bool Logging::InternalInitialize() { | |
| 448 __try { | |
| 449 if (logging_shutdown_ == true) { | |
| 450 OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - " | |
| 451 L"Calling the logging system after " | |
| 452 L"it has been shut down \n", | |
| 453 GetProcName())); | |
| 454 return false; | |
| 455 } | |
| 456 | |
| 457 if (logging_initialized_ == true) { | |
| 458 return true; | |
| 459 } | |
| 460 // If something called by this method is attempting to do logging, | |
| 461 // just ignore it. The cost/benefit ratio is too high to do otherwise. | |
| 462 if (is_initializing_) { | |
| 463 return false; | |
| 464 } | |
| 465 is_initializing_ = true; | |
| 466 | |
| 467 // Read the initial settings from the config file. | |
| 468 ReadLoggingSettings(); | |
| 469 | |
| 470 // Initialize logging system if enabled at start. | |
| 471 if (logging_enabled_) { | |
| 472 logging_initialized_ = ConfigureLogging(); | |
| 473 } | |
| 474 } __except(SehNoMinidump(GetExceptionCode(), | |
| 475 GetExceptionInformation(), | |
| 476 __FILE__, | |
| 477 __LINE__, | |
| 478 true)) { | |
| 479 OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n"); | |
| 480 logging_initialized_ = false; | |
| 481 return false; | |
| 482 } | |
| 483 | |
| 484 is_initializing_ = false; | |
| 485 return true; | |
| 486 } | |
| 487 | |
| 488 bool Logging::InitializeLogging() { | |
| 489 // Double-checked locking idiom is broken, especially on multicore machines. | |
| 490 // TODO(omaha): understand how this works and fix it. | |
| 491 if (logging_shutdown_ == true) { | |
| 492 OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - Calling the logging " | |
| 493 L"system after it has been shut down \n", | |
| 494 GetProcName())); | |
| 495 return false; | |
| 496 } | |
| 497 | |
| 498 if (logging_initialized_ == true) { | |
| 499 return true; | |
| 500 } | |
| 501 | |
| 502 // Acquire the lock outside the try/except block so we'll always release it. | |
| 503 __mutexScope(lock_); | |
| 504 return InternalInitialize(); | |
| 505 } | |
| 506 | |
| 507 // Enables/disables the logging mechanism. Allows turning logging on/off | |
| 508 // in mid-run. | |
| 509 // TODO(omaha): same comment as for the destructor. | |
| 510 void Logging::EnableLogging() { | |
| 511 if (!InitializeLogging()) { | |
| 512 return; | |
| 513 } | |
| 514 | |
| 515 // Acquire the lock outside the try/except block so we'll always release it. | |
| 516 lock_.Lock(); | |
| 517 | |
| 518 __try { | |
| 519 if (!logging_enabled_) { | |
| 520 ConfigureLogging(); | |
| 521 logging_enabled_ = true; | |
| 522 } | |
| 523 } __except(SehNoMinidump(GetExceptionCode(), | |
| 524 GetExceptionInformation(), | |
| 525 __FILE__, | |
| 526 __LINE__, | |
| 527 true)) { | |
| 528 OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n"); | |
| 529 logging_enabled_ = false; | |
| 530 } | |
| 531 | |
| 532 lock_.Unlock(); | |
| 533 } | |
| 534 | |
| 535 void Logging::DisableLogging() { | |
| 536 if (!InitializeLogging()) { | |
| 537 return; | |
| 538 } | |
| 539 | |
| 540 // Acquire the lock outside the try/except block so we'll always release it. | |
| 541 lock_.Lock(); | |
| 542 | |
| 543 __try { | |
| 544 if (logging_enabled_) { | |
| 545 logging_enabled_ = false; | |
| 546 UnconfigureLogging(); | |
| 547 } | |
| 548 } __except(SehNoMinidump(GetExceptionCode(), | |
| 549 GetExceptionInformation(), | |
| 550 __FILE__, | |
| 551 __LINE__, | |
| 552 true)) { | |
| 553 OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n"); | |
| 554 logging_enabled_ = false; | |
| 555 } | |
| 556 | |
| 557 lock_.Unlock(); | |
| 558 } | |
| 559 | |
| 560 // Checks if logging is enabled - and updates logging settings from the | |
| 561 // configuration file every kLogSettingsCheckInterval seconds. | |
| 562 bool Logging::IsLoggingEnabled() { | |
| 563 if (!InitializeLogging()) { | |
| 564 return false; | |
| 565 } | |
| 566 | |
| 567 // Dynamic update - including reading a new value of logging_enabled_. | |
| 568 bool prev_logging_enabled = logging_enabled_; | |
| 569 if (GetCurrent100NSTime() > | |
| 570 g_last_category_check_time + kLogSettingsCheckInterval) { | |
| 571 ReadLoggingSettings(); | |
| 572 } | |
| 573 | |
| 574 // If enabled state has changed either enable or disable logging. | |
| 575 if (prev_logging_enabled != logging_enabled_) { | |
| 576 if (logging_enabled_) { | |
| 577 EnableLogging(); | |
| 578 } else { | |
| 579 DisableLogging(); | |
| 580 } | |
| 581 } | |
| 582 | |
| 583 return logging_enabled_; | |
| 584 } | |
| 585 | |
| 586 bool Logging::IsLoggingAlreadyEnabled() const { | |
| 587 return logging_enabled_; | |
| 588 } | |
| 589 | |
| 590 void Logging::ForceShowTimestamp(bool force_show_time) { | |
| 591 force_show_time_ = show_time_ = force_show_time; | |
| 592 } | |
| 593 | |
| 594 // Get category level | |
| 595 LogLevel Logging::GetCatLevel(LogCategory category) const { | |
| 596 if (!IsLoggingAlreadyEnabled()) { | |
| 597 return kDefaultLogLevel; | |
| 598 } | |
| 599 | |
| 600 if (category >= LC_MAX_CAT) { | |
| 601 return kDefaultLogLevel; | |
| 602 } | |
| 603 | |
| 604 return category_list_[category].log_level; | |
| 605 } | |
| 606 | |
| 607 // Check if logging is enabled for a given category and level | |
| 608 DWORD Logging::IsCatLevelEnabled(LogCategory category, LogLevel level) { | |
| 609 if (!IsLoggingEnabled()) { | |
| 610 return 0; | |
| 611 } | |
| 612 | |
| 613 if (category >= LC_MAX_CAT) { | |
| 614 return 0; | |
| 615 } | |
| 616 | |
| 617 // If the config value is to log: then log to all writers. | |
| 618 if (category_list_[category].enabled && | |
| 619 level <= category_list_[category].log_level) { | |
| 620 return static_cast<DWORD>(all_writers_mask); | |
| 621 } | |
| 622 | |
| 623 // Check each of the registered loggers to see if they want to override the | |
| 624 // negative config value. | |
| 625 DWORD mask = 0; | |
| 626 for (int i = num_writers_ - 1; i >= 0; --i) { | |
| 627 mask <<= 1; | |
| 628 if (writers_[i] && writers_[i]->IsCatLevelEnabled(category, level)) { | |
| 629 mask |= 1; | |
| 630 } | |
| 631 } | |
| 632 | |
| 633 return mask; | |
| 634 } | |
| 635 | |
| 636 // TODO(omaha): For now this is hard coded and there is no way to override | |
| 637 // writing other log categories into the history. Add a store_in_history | |
| 638 // boolean into the CategoryInfo struct that allows reading from the config | |
| 639 // file. This will enable other log categories to get buffered in the history. | |
| 640 bool Logging::IsCategoryEnabledForBuffering(LogCategory cat) { | |
| 641 return cat == LC_REPORT; | |
| 642 } | |
| 643 | |
| 644 void Logging::LogMessage(LogCategory cat, LogLevel level, | |
| 645 const wchar_t* fmt, ...) { | |
| 646 va_list args; | |
| 647 va_start(args, fmt); | |
| 648 LogMessageVA(cat, level, fmt, args); | |
| 649 va_end(args); | |
| 650 } | |
| 651 | |
| 652 void Logging::LogMessageVA(LogCategory cat, LogLevel level, | |
| 653 const wchar_t* fmt, va_list args) { | |
| 654 LogMessageMaskedVA(static_cast<DWORD>(all_writers_mask), | |
| 655 cat, | |
| 656 level, | |
| 657 fmt, | |
| 658 args); | |
| 659 } | |
| 660 | |
| 661 void Logging::InternalLogMessageMaskedVA(DWORD writer_mask, | |
| 662 LogCategory cat, | |
| 663 LogLevel level, | |
| 664 CString* log_buffer, | |
| 665 CString* prefix, | |
| 666 const wchar_t* fmt, | |
| 667 va_list args) { | |
| 668 __try { | |
| 669 // Initial buffer size in characters. | |
| 670 // It will adjust dynamically if the message is bigger. | |
| 671 DWORD buffer_size = 512; | |
| 672 | |
| 673 // Count of chars / bytes written. | |
| 674 int num_chars = 0; | |
| 675 bool result = false; | |
| 676 | |
| 677 // Write the message in the buffer. | |
| 678 // Dynamically adjust the size to hold the entire message. | |
| 679 | |
| 680 while ((num_chars = _vsnwprintf_s( | |
| 681 log_buffer->GetBufferSetLength(buffer_size), | |
| 682 buffer_size, | |
| 683 _TRUNCATE, | |
| 684 fmt, | |
| 685 args)) == -1) { | |
| 686 // Truncate if the message is too big. | |
| 687 if (buffer_size >= kMaxLogMessageSize) { | |
| 688 num_chars = buffer_size; | |
| 689 break; | |
| 690 } | |
| 691 | |
| 692 // Get a buffer that is big enough. | |
| 693 buffer_size *= 2; | |
| 694 } | |
| 695 | |
| 696 log_buffer->ReleaseBuffer(num_chars); | |
| 697 | |
| 698 FormatLinePrefix(show_time_, proc_name_, *prefix); | |
| 699 | |
| 700 // Log the message. | |
| 701 OutputInfo info(cat, level, *prefix, *log_buffer); | |
| 702 OutputMessage(writer_mask, &info); | |
| 703 } __except(SehSendMinidump(GetExceptionCode(), | |
| 704 GetExceptionInformation(), | |
| 705 kMinsTo100ns)) { | |
| 706 OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n"); | |
| 707 OutputDebugString(fmt); | |
| 708 OutputDebugString(L"\n\r"); | |
| 709 } | |
| 710 } | |
| 711 | |
| 712 void Logging::LogMessageMaskedVA(DWORD writer_mask, | |
| 713 LogCategory cat, | |
| 714 LogLevel level, | |
| 715 const wchar_t* fmt, | |
| 716 va_list args) { | |
| 717 if (!fmt) { | |
| 718 return; | |
| 719 } | |
| 720 | |
| 721 if (writer_mask == 0 && level > kMaxLevelToStoreInLogHistory) { | |
| 722 return; | |
| 723 } | |
| 724 | |
| 725 CString log_buffer; // The buffer for formatted log messages. | |
| 726 CString prefix; | |
| 727 | |
| 728 int i = 0; | |
| 729 while (++i <= kNumLockRetries) { | |
| 730 if (lock_.Lock(0)) { | |
| 731 InternalLogMessageMaskedVA(writer_mask, cat, level, &log_buffer, | |
| 732 &prefix, fmt, args); | |
| 733 lock_.Unlock(); | |
| 734 break; | |
| 735 } | |
| 736 | |
| 737 Sleep(kLockRetryDelayMs); | |
| 738 } | |
| 739 | |
| 740 if (i > kNumLockRetries) { | |
| 741 OutputDebugStringA("LOG_SYSTEM: Couldn't acquire lock - "); | |
| 742 OutputDebugString(fmt); | |
| 743 OutputDebugString(L"\n\r"); | |
| 744 } | |
| 745 } | |
| 746 | |
| 747 void Logging::OutputMessage(DWORD writer_mask, LogCategory cat, LogLevel level, | |
| 748 const wchar_t* msg1, const wchar_t* msg2) { | |
| 749 OutputInfo info(cat, level, msg1, msg2); | |
| 750 OutputMessage(writer_mask, &info); | |
| 751 } | |
| 752 | |
| 753 // Store log message in in-memory history buffer. | |
| 754 void Logging::StoreInHistory(const OutputInfo* output_info) { | |
| 755 AppendToHistory(output_info->msg1); | |
| 756 AppendToHistory(output_info->msg2); | |
| 757 AppendToHistory(L"\r\n"); | |
| 758 } | |
| 759 | |
| 760 // Append string to in-memory history buffer. | |
| 761 // history_buffer_next_idx points to the next index to write at, | |
| 762 // thus it should always be in (0 - kHistoryBufferEndIdx). | |
| 763 void Logging::AppendToHistory(const wchar_t* msg) { | |
| 764 int msg_len = wcslen(msg); | |
| 765 if (msg_len == 0) { | |
| 766 return; | |
| 767 } | |
| 768 | |
| 769 if (msg_len >= kMaxHistoryBufferSize) { | |
| 770 // Write the first kMaxHistoryBufferSize chars. | |
| 771 memcpy(history_buffer, msg, kMaxHistoryBufferSize * sizeof(TCHAR)); | |
| 772 history_buffer_next_idx = 0; | |
| 773 history_buffer_full = true; | |
| 774 return; | |
| 775 } | |
| 776 | |
| 777 // Determine if the message fits into the portion of the buffer after | |
| 778 // history_buffer_next_idx. | |
| 779 if (msg_len + history_buffer_next_idx < kMaxHistoryBufferSize) { | |
| 780 memcpy(history_buffer + history_buffer_next_idx, msg, | |
| 781 msg_len * sizeof(TCHAR)); | |
| 782 history_buffer_next_idx += msg_len; | |
| 783 return; | |
| 784 } | |
| 785 | |
| 786 // Have to split the input message into the part that fits in | |
| 787 // history_buffer_next_idx to kMaxHistoryBufferSize and the remaining message. | |
| 788 int msg_first_part_len = kMaxHistoryBufferSize - history_buffer_next_idx; | |
| 789 int msg_second_part_len = msg_len - msg_first_part_len; | |
| 790 memcpy(history_buffer + history_buffer_next_idx, | |
| 791 msg, | |
| 792 msg_first_part_len * sizeof(TCHAR)); | |
| 793 | |
| 794 history_buffer_full = true; | |
| 795 history_buffer_next_idx = msg_second_part_len; | |
| 796 if (msg_second_part_len) { | |
| 797 memcpy(history_buffer, | |
| 798 msg + msg_first_part_len, | |
| 799 msg_second_part_len * sizeof(TCHAR)); | |
| 800 } | |
| 801 } | |
| 802 | |
| 803 // Retrieve in-memory history buffer. | |
| 804 CString Logging::GetHistory() { | |
| 805 CString history; | |
| 806 | |
| 807 if (history_buffer_full) { | |
| 808 history.Append(history_buffer + history_buffer_next_idx, | |
| 809 kMaxHistoryBufferSize - history_buffer_next_idx); | |
| 810 } | |
| 811 history.Append(history_buffer, history_buffer_next_idx); | |
| 812 | |
| 813 // Reset the history buffer to the original state. | |
| 814 history_buffer_next_idx = 0; | |
| 815 history_buffer_full = false; | |
| 816 memset(history_buffer, 0, kMaxHistoryBufferSize * sizeof(TCHAR)); | |
| 817 | |
| 818 return history; | |
| 819 } | |
| 820 | |
| 821 void Logging::OutputMessage(DWORD writer_mask, | |
| 822 const OutputInfo* output_info) { | |
| 823 if (output_info->level <= kMaxLevelToStoreInLogHistory && | |
| 824 IsCategoryEnabledForBuffering(output_info->category)) { | |
| 825 StoreInHistory(output_info); | |
| 826 } | |
| 827 | |
| 828 for (int i = 0; i < num_writers_; ++i) { | |
| 829 if (writer_mask & 1) { | |
| 830 __try { | |
| 831 if (logging_enabled_ || writers_[i]->WantsToLogRegardless()) { | |
| 832 writers_[i]->OutputMessage(output_info); | |
| 833 } | |
| 834 } | |
| 835 __except(SehNoMinidump(GetExceptionCode(), | |
| 836 GetExceptionInformation(), | |
| 837 __FILE__, | |
| 838 __LINE__, | |
| 839 true)) { | |
| 840 // Just eat errors that happen from within the LogWriters. This is | |
| 841 // important so that if such an error happens when OutputMessage is | |
| 842 // called from debugASSERT we don't go recursively into more | |
| 843 // error handling ... | |
| 844 } | |
| 845 } | |
| 846 writer_mask >>= 1; | |
| 847 } | |
| 848 } | |
| 849 | |
| 850 bool Logging::InternalRegisterWriter(LogWriter* log_writer) { | |
| 851 if (num_writers_ >= max_writers) { | |
| 852 return false; | |
| 853 } | |
| 854 writers_[num_writers_++] = log_writer; | |
| 855 return true; | |
| 856 } | |
| 857 | |
| 858 bool Logging::RegisterWriter(LogWriter* log_writer) { | |
| 859 if (!InternalRegisterWriter(log_writer)) { | |
| 860 return false; | |
| 861 } | |
| 862 if (log_writer->WantsToLogRegardless()) { | |
| 863 EnableLogging(); | |
| 864 } | |
| 865 return true; | |
| 866 } | |
| 867 | |
| 868 bool Logging::InternalUnregisterWriter(LogWriter* log_writer) { | |
| 869 bool result = false; | |
| 870 for (int i = 0; i < num_writers_; ++i) { | |
| 871 if (writers_[i] == log_writer) { | |
| 872 // Replace this entry with last entry in array, then truncate. | |
| 873 writers_[i] = writers_[--num_writers_]; | |
| 874 result = true; | |
| 875 break; | |
| 876 } | |
| 877 } | |
| 878 return result; | |
| 879 } | |
| 880 | |
| 881 bool Logging::UnregisterWriter(LogWriter* log_writer) { | |
| 882 if (!InternalUnregisterWriter(log_writer)) { | |
| 883 return false; | |
| 884 } | |
| 885 if (num_writers_ == 0) { | |
| 886 DisableLogging(); | |
| 887 } | |
| 888 return true; | |
| 889 } | |
| 890 | |
| 891 // The primary configuration file under %PROGRAMFILES%\Google\Update is | |
| 892 // removed on uninstall. This is midly inconvenient during development | |
| 893 // therefore a fallback location for the configuration file is desired. | |
| 894 CString Logging::GetCurrentConfigurationFilePath() const { | |
| 895 if (!config_file_path_.IsEmpty() && | |
| 896 File::Exists(config_file_path_)) { | |
| 897 return config_file_path_; | |
| 898 } else { | |
| 899 return L""; | |
| 900 } | |
| 901 } | |
| 902 | |
| 903 CString Logging::GetConfigurationFilePath() const { | |
| 904 CString file_path; | |
| 905 CString system_drive = GetEnvironmentVariableAsString(_T("SystemDrive")); | |
| 906 if (!system_drive.IsEmpty()) { | |
| 907 file_path = system_drive; | |
| 908 file_path += L"\\"; | |
| 909 } | |
| 910 return file_path + kLogConfigFileName; | |
| 911 } | |
| 912 | |
| 913 LogWriter::LogWriter() { | |
| 914 } | |
| 915 | |
| 916 LogWriter::~LogWriter() { | |
| 917 } | |
| 918 | |
| 919 void LogWriter::Cleanup() {} | |
| 920 | |
| 921 bool LogWriter::WantsToLogRegardless() const { return false; } | |
| 922 | |
| 923 bool LogWriter::IsCatLevelEnabled(LogCategory, LogLevel) const { | |
| 924 return false; | |
| 925 } | |
| 926 | |
| 927 void LogWriter::OutputMessage(const OutputInfo*) { } | |
| 928 | |
| 929 bool LogWriter::Register() { | |
| 930 Logging* logger = GetLogging(); | |
| 931 if (logger) { | |
| 932 return logger->RegisterWriter(this); | |
| 933 } else { | |
| 934 return false; | |
| 935 } | |
| 936 } | |
| 937 | |
| 938 bool LogWriter::Unregister() { | |
| 939 Logging* logger = GetLogging(); | |
| 940 if (logger) { | |
| 941 return logger->RegisterWriter(this); | |
| 942 } else { | |
| 943 return false; | |
| 944 } | |
| 945 } | |
| 946 | |
| 947 // FileLogWriter | |
| 948 | |
| 949 FileLogWriter* FileLogWriter::Create(const wchar_t* file_name, bool append) { | |
| 950 return new FileLogWriter(file_name, append); | |
| 951 } | |
| 952 | |
| 953 FileLogWriter::FileLogWriter(const wchar_t* file_name, bool append) | |
| 954 : initialized_(false), | |
| 955 valid_(false), | |
| 956 file_name_(file_name), | |
| 957 log_file_mutex_(NULL), | |
| 958 log_file_(NULL), | |
| 959 append_(append), | |
| 960 max_file_size_(kDefaultMaxLogFileSize), | |
| 961 log_file_wide_(kDefaultLogFileWide) { | |
| 962 Logging* logger = GetLogging(); | |
| 963 if (logger) { | |
| 964 CString config_file_path = logger->GetCurrentConfigurationFilePath(); | |
| 965 if (!config_file_path.IsEmpty()) { | |
| 966 max_file_size_ = ::GetPrivateProfileInt( | |
| 967 kConfigSectionLoggingSettings, | |
| 968 kConfigAttrMaxLogFileSize, | |
| 969 kDefaultMaxLogFileSize, | |
| 970 config_file_path); | |
| 971 log_file_wide_ = ::GetPrivateProfileInt( | |
| 972 kConfigSectionLoggingSettings, | |
| 973 kConfigAttrLogFileWide, | |
| 974 kDefaultLogFileWide, | |
| 975 config_file_path) == 0 ? false : true; | |
| 976 } else { | |
| 977 max_file_size_ = kDefaultMaxLogFileSize; | |
| 978 log_file_wide_ = kDefaultLogFileWide; | |
| 979 } | |
| 980 proc_name_ = logger->proc_name(); | |
| 981 } | |
| 982 } | |
| 983 | |
| 984 FileLogWriter::~FileLogWriter() { | |
| 985 // TODO(omaha): Figure out a way to pass the proc_name - and possibly | |
| 986 // the show_time var - into here. | |
| 987 Logging* logger = GetLogging(); | |
| 988 if (logger && logger->IsLoggingAlreadyEnabled()) { | |
| 989 // OutputInfo info(LEVEL_WARNING, NULL, kEndOfLogMessage); | |
| 990 // OutputMessage(&info); | |
| 991 } | |
| 992 Cleanup(); | |
| 993 } | |
| 994 | |
| 995 void FileLogWriter::Initialize() { | |
| 996 if (initialized_) { | |
| 997 return; | |
| 998 } | |
| 999 | |
| 1000 initialized_ = true; | |
| 1001 | |
| 1002 bool already_created = CreateLoggingMutex(); | |
| 1003 if (!log_file_mutex_) { | |
| 1004 return; | |
| 1005 } | |
| 1006 | |
| 1007 if (already_created) { | |
| 1008 append_ = true; | |
| 1009 } | |
| 1010 | |
| 1011 if (!GetMutex()) { | |
| 1012 ::OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: " | |
| 1013 L"Could not acquire logging mutex %s\n", | |
| 1014 proc_name_, | |
| 1015 log_file_mutex_name_)); | |
| 1016 return; | |
| 1017 } | |
| 1018 | |
| 1019 CreateLoggingFile(); | |
| 1020 if (!log_file_) { | |
| 1021 ::OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: " | |
| 1022 L"Could not create logging file %s\n", | |
| 1023 proc_name_, | |
| 1024 file_name_)); | |
| 1025 valid_ = false; | |
| 1026 } | |
| 1027 | |
| 1028 valid_ = true; | |
| 1029 ReleaseMutex(); | |
| 1030 } | |
| 1031 | |
| 1032 void FileLogWriter::Cleanup() { | |
| 1033 if (log_file_) { | |
| 1034 ::CloseHandle(log_file_); | |
| 1035 } | |
| 1036 if (log_file_mutex_) { | |
| 1037 ::ReleaseMutex(log_file_mutex_); | |
| 1038 ::CloseHandle(log_file_mutex_); | |
| 1039 } | |
| 1040 } | |
| 1041 | |
| 1042 bool FileLogWriter::CreateLoggingMutex() { | |
| 1043 log_file_mutex_name_ = StripBackslashes(kLoggingMutexName L"_" + file_name_); | |
| 1044 // TODO(omaha): I don't see where this class is used, but I guess the | |
| 1045 // caller is always in the same context. We should use the default security | |
| 1046 // here. If the caller can be in different contexts (System Service, Usermode | |
| 1047 // applications, etc), then we should revisit this code to give access only | |
| 1048 // to those who really need it. What if a malicious piece a code decide | |
| 1049 // to get the mutex and lock it? If it happens, everytime you try to log | |
| 1050 // something, the thread would hang for 500ms and then fail to log the | |
| 1051 // message. | |
| 1052 CSecurityDesc sd; | |
| 1053 GetEveryoneDaclSecurityDescriptor(&sd, GENERIC_ALL, GENERIC_ALL); | |
| 1054 CSecurityAttributes sa(sd); | |
| 1055 log_file_mutex_ = CreateMutexWithSyncAccess(log_file_mutex_name_, &sa); | |
| 1056 if (log_file_mutex_) { | |
| 1057 return ERROR_ALREADY_EXISTS == ::GetLastError(); | |
| 1058 } | |
| 1059 return false; | |
| 1060 } | |
| 1061 | |
| 1062 bool FileLogWriter::CreateLoggingFile() { | |
| 1063 uint32 file_size(0); | |
| 1064 File::GetFileSizeUnopen(file_name_, &file_size); | |
| 1065 if (file_size > max_file_size_) { | |
| 1066 ArchiveLoggingFile(); | |
| 1067 } | |
| 1068 log_file_ = ::CreateFile(file_name_, | |
| 1069 GENERIC_WRITE, | |
| 1070 FILE_SHARE_WRITE | FILE_SHARE_READ, | |
| 1071 NULL, | |
| 1072 append_ ? OPEN_ALWAYS : CREATE_ALWAYS, | |
| 1073 FILE_ATTRIBUTE_NORMAL, | |
| 1074 NULL); | |
| 1075 if (log_file_ == INVALID_HANDLE_VALUE) { | |
| 1076 // The code in this file is written with the assumption that log_file_ is | |
| 1077 // NULL on creation errors. The easy fix is to set it to NULL here. The | |
| 1078 // long term fix should be implementing it in terms of a smart handle. | |
| 1079 log_file_ = NULL; | |
| 1080 return false; | |
| 1081 } | |
| 1082 | |
| 1083 // Allow users to read, write, and delete the log file. | |
| 1084 ACCESS_MASK mask = GENERIC_READ | GENERIC_WRITE | DELETE; | |
| 1085 CDacl dacl; | |
| 1086 if (dacl.AddAllowedAce(ATL::Sids::Users(), mask)) { | |
| 1087 AtlSetDacl(file_name_, SE_FILE_OBJECT, dacl); | |
| 1088 } | |
| 1089 | |
| 1090 // Insert a BOM in the newly created file. | |
| 1091 if (GetLastError() != ERROR_ALREADY_EXISTS && log_file_wide_) { | |
| 1092 DWORD num = 0; | |
| 1093 ::WriteFile(log_file_, &kUnicodeBom, sizeof(kUnicodeBom), &num, NULL); | |
| 1094 } | |
| 1095 return true; | |
| 1096 } | |
| 1097 | |
| 1098 bool FileLogWriter::TruncateLoggingFile() { | |
| 1099 DWORD share_mode = FILE_SHARE_WRITE; | |
| 1100 HANDLE log_file = ::CreateFile(file_name_, | |
| 1101 GENERIC_WRITE, | |
| 1102 FILE_SHARE_WRITE | FILE_SHARE_READ, | |
| 1103 NULL, | |
| 1104 TRUNCATE_EXISTING, | |
| 1105 FILE_ATTRIBUTE_NORMAL, | |
| 1106 NULL); | |
| 1107 if (log_file_ == INVALID_HANDLE_VALUE) { | |
| 1108 return false; | |
| 1109 } | |
| 1110 | |
| 1111 // Insert a BOM in the newly created file. | |
| 1112 if (log_file_wide_) { | |
| 1113 DWORD num = 0; | |
| 1114 ::WriteFile(log_file, &kUnicodeBom, sizeof(kUnicodeBom), &num, NULL); | |
| 1115 } | |
| 1116 ::CloseHandle(log_file); | |
| 1117 return true; | |
| 1118 } | |
| 1119 | |
| 1120 bool FileLogWriter::ArchiveLoggingFile() { | |
| 1121 ::OutputDebugString(L"LOG_SYSTEM: trying to move log file to backup\n"); | |
| 1122 CString backup_file_name = file_name_ + L".bak"; | |
| 1123 HRESULT hr = File::Move(file_name_, backup_file_name, true); | |
| 1124 if (FAILED(hr)) { | |
| 1125 ::OutputDebugString(L"LOG_SYSTEM: failed to move log file to backup\n"); | |
| 1126 | |
| 1127 // Trying to move the log file when loggers have it open returns | |
| 1128 // ERROR_SHARING_VIOLATION. Each call to MoveFileAfterReboot inserts the | |
| 1129 // file into PendingFileRenames list. Moving files at reboot requires the | |
| 1130 // user to be either the LocalSystem account or in the Administrators | |
| 1131 // group. | |
| 1132 if (!IsArchivePending()) { | |
| 1133 File::MoveAfterReboot(file_name_, backup_file_name); | |
| 1134 } | |
| 1135 return false; | |
| 1136 } | |
| 1137 return true; | |
| 1138 } | |
| 1139 | |
| 1140 bool FileLogWriter::IsArchivePending() { | |
| 1141 // We look at the PendingFileRenameOperations to see if our log file is | |
| 1142 // pending a rename. The list is a REG_MULTI_SZ, which is a sequence of | |
| 1143 // null-terminated strings, terminated by an empty string "\0". | |
| 1144 // The strings have the structure: | |
| 1145 // \??\file1\0!\??\file2\0\file3\0\0...\0\0. where file1 is to be renamed to | |
| 1146 // file2 and file3 is to be deleted. | |
| 1147 // It is valid for the PFR list to include an empty string in the middle | |
| 1148 // of the sequence. | |
| 1149 const wchar_t sub_key_name[] = L"SYSTEM\\CurrentControlSet\\Control\\" | |
| 1150 L"Session Manager"; | |
| 1151 HKEY key = NULL; | |
| 1152 int res = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, | |
| 1153 sub_key_name, | |
| 1154 0, | |
| 1155 KEY_READ, | |
| 1156 &key); | |
| 1157 if (res != ERROR_SUCCESS) { | |
| 1158 return false; | |
| 1159 } | |
| 1160 DWORD bytes = 0; | |
| 1161 DWORD type = REG_MULTI_SZ; | |
| 1162 res = ::RegQueryValueEx(key, | |
| 1163 L"PendingFileRenameOperations", | |
| 1164 0, | |
| 1165 &type, | |
| 1166 NULL, | |
| 1167 &bytes); | |
| 1168 if (!(res == ERROR_SUCCESS && type == REG_MULTI_SZ)) { | |
| 1169 return false; | |
| 1170 } | |
| 1171 scoped_array<byte> buf(new byte[bytes]); | |
| 1172 memset(buf.get(), 0, bytes); | |
| 1173 res = ::RegQueryValueEx(key, | |
| 1174 L"PendingFileRenameOperations", | |
| 1175 0, | |
| 1176 NULL, | |
| 1177 buf.get(), | |
| 1178 &bytes); | |
| 1179 if (res != ERROR_SUCCESS) { | |
| 1180 return false; | |
| 1181 } | |
| 1182 const wchar_t* multi_str = reinterpret_cast<const wchar_t*>(buf.get()); | |
| 1183 size_t count = bytes / sizeof(*multi_str); | |
| 1184 const size_t kMaxRegistryValueLen = 1024 * 1024; // 1MB | |
| 1185 if (!(count >= 2 && | |
| 1186 count < kMaxRegistryValueLen && | |
| 1187 multi_str[count - 2] == L'\0' && | |
| 1188 multi_str[count - 1] == L'\0')) { | |
| 1189 return false; | |
| 1190 } | |
| 1191 // The file names in the PFR list are prefixed by \??\. | |
| 1192 CString file_name = L"\\??\\" + file_name_; | |
| 1193 return FindFirstInMultiString(multi_str, count, file_name) != -1; | |
| 1194 } | |
| 1195 | |
| 1196 int FileLogWriter::FindFirstInMultiString(const wchar_t* multi_str, | |
| 1197 size_t count, | |
| 1198 const wchar_t* str) { | |
| 1199 const wchar_t* p = multi_str; | |
| 1200 size_t i = 0; | |
| 1201 while (i < count) { | |
| 1202 p = multi_str + i; | |
| 1203 if (lstrcmp(p, str) == 0) { | |
| 1204 return i; | |
| 1205 } else { | |
| 1206 size_t len = lstrlen(p); | |
| 1207 i += len + 1; | |
| 1208 } | |
| 1209 } | |
| 1210 return -1; | |
| 1211 } | |
| 1212 | |
| 1213 void FileLogWriter::OutputMessage(const OutputInfo* output_info) { | |
| 1214 if (!initialized_) { | |
| 1215 Initialize(); | |
| 1216 } | |
| 1217 | |
| 1218 if (!valid_) { | |
| 1219 return; | |
| 1220 } | |
| 1221 | |
| 1222 // Acquire the mutex. | |
| 1223 if (!GetMutex()) { | |
| 1224 return; | |
| 1225 } | |
| 1226 | |
| 1227 // Move to end of file. | |
| 1228 DWORD pos = ::SetFilePointer(log_file_, 0, NULL, FILE_END); | |
| 1229 int64 stop_gap_file_size = kStopGapLogFileSizeFactor * | |
| 1230 static_cast<int64>(max_file_size_); | |
| 1231 if (pos >= stop_gap_file_size) { | |
| 1232 if (!TruncateLoggingFile()) { | |
| 1233 // Logging stops until the log can be archived over since we do not | |
| 1234 // want to overfill the disk. | |
| 1235 return; | |
| 1236 } | |
| 1237 } | |
| 1238 pos = ::SetFilePointer(log_file_, 0, NULL, FILE_END); | |
| 1239 | |
| 1240 // Write the date, followed by a CRLF | |
| 1241 DWORD written_size = 0; | |
| 1242 if (output_info->msg1) { | |
| 1243 if (log_file_wide_) { | |
| 1244 ::WriteFile(log_file_, output_info->msg1, | |
| 1245 lstrlen(output_info->msg1) * sizeof(wchar_t), &written_size, | |
| 1246 NULL); | |
| 1247 } else { | |
| 1248 CStringA msg(WideToAnsiDirect(output_info->msg1)); | |
| 1249 ::WriteFile(log_file_, msg.GetString(), msg.GetLength(), &written_size, | |
| 1250 NULL); | |
| 1251 } | |
| 1252 } | |
| 1253 | |
| 1254 if (output_info->msg2) { | |
| 1255 if (log_file_wide_) { | |
| 1256 ::WriteFile(log_file_, output_info->msg2, | |
| 1257 lstrlen(output_info->msg2) * sizeof(wchar_t), &written_size, | |
| 1258 NULL); | |
| 1259 } else { | |
| 1260 CStringA msg(WideToAnsiDirect(output_info->msg2)); | |
| 1261 ::WriteFile(log_file_, msg.GetString(), msg.GetLength(), &written_size, | |
| 1262 NULL); | |
| 1263 } | |
| 1264 } | |
| 1265 | |
| 1266 if (log_file_wide_) { | |
| 1267 ::WriteFile(log_file_, L"\r\n", 2 * sizeof(wchar_t), &written_size, NULL); | |
| 1268 } else { | |
| 1269 ::WriteFile(log_file_, "\r\n", 2, &written_size, NULL); | |
| 1270 } | |
| 1271 | |
| 1272 ReleaseMutex(); | |
| 1273 } | |
| 1274 | |
| 1275 bool FileLogWriter::GetMutex() { | |
| 1276 if (!log_file_mutex_) { | |
| 1277 return false; | |
| 1278 } | |
| 1279 | |
| 1280 DWORD res = ::WaitForSingleObject(log_file_mutex_, kMaxMutexWaitTimeMs); | |
| 1281 if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) { | |
| 1282 ::OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: " | |
| 1283 L"Could not acquire logging mutex %s\n", | |
| 1284 proc_name_, log_file_mutex_name_)); | |
| 1285 valid_ = false; | |
| 1286 return false; | |
| 1287 } | |
| 1288 | |
| 1289 return true; | |
| 1290 } | |
| 1291 | |
| 1292 void FileLogWriter::ReleaseMutex() { | |
| 1293 if (log_file_mutex_) { | |
| 1294 ::ReleaseMutex(log_file_mutex_); | |
| 1295 } | |
| 1296 } | |
| 1297 | |
| 1298 // OutputDebugStringLogWriter. | |
| 1299 OutputDebugStringLogWriter* OutputDebugStringLogWriter::Create() { | |
| 1300 return new OutputDebugStringLogWriter(); | |
| 1301 } | |
| 1302 | |
| 1303 OutputDebugStringLogWriter::OutputDebugStringLogWriter() {} | |
| 1304 | |
| 1305 OutputDebugStringLogWriter::~OutputDebugStringLogWriter() { | |
| 1306 Logging* logger = GetLogging(); | |
| 1307 if (logger && logger->IsLoggingAlreadyEnabled()) { | |
| 1308 // OutputInfo info(LEVEL_WARNING, NULL, kEndOfLogMessage); | |
| 1309 // OutputMessage(&info); | |
| 1310 } | |
| 1311 Cleanup(); | |
| 1312 } | |
| 1313 | |
| 1314 void OutputDebugStringLogWriter::OutputMessage(const OutputInfo* output_info) { | |
| 1315 // Combine everything into one string so that messages coming from | |
| 1316 // multiple threads don't get interleaved. | |
| 1317 ::OutputDebugString(SPRINTF(L"%s%s\n", output_info->msg1, output_info->msg2)); | |
| 1318 } | |
| 1319 | |
| 1320 // OverrideConfigLogWriter. | |
| 1321 OverrideConfigLogWriter* OverrideConfigLogWriter::Create(LogCategory category, | |
| 1322 LogLevel level, LogWriter* log_writer, bool force_logging_enabled) { | |
| 1323 return new OverrideConfigLogWriter(category, | |
| 1324 level, | |
| 1325 log_writer, | |
| 1326 force_logging_enabled); | |
| 1327 } | |
| 1328 | |
| 1329 OverrideConfigLogWriter::OverrideConfigLogWriter(LogCategory category, | |
| 1330 LogLevel level, | |
| 1331 LogWriter* log_writer, | |
| 1332 bool force_logging_enabled) | |
| 1333 : category_(category), | |
| 1334 level_(level), | |
| 1335 log_writer_(log_writer), | |
| 1336 force_logging_enabled_(force_logging_enabled) {} | |
| 1337 | |
| 1338 void OverrideConfigLogWriter::Cleanup() { | |
| 1339 if (log_writer_) { | |
| 1340 delete log_writer_; | |
| 1341 } | |
| 1342 } | |
| 1343 | |
| 1344 bool OverrideConfigLogWriter::WantsToLogRegardless() const { | |
| 1345 return force_logging_enabled_; | |
| 1346 } | |
| 1347 | |
| 1348 bool OverrideConfigLogWriter::IsCatLevelEnabled(LogCategory category, | |
| 1349 LogLevel level) const { | |
| 1350 if (category != category_) { | |
| 1351 return false; | |
| 1352 } | |
| 1353 if (level > level_) { | |
| 1354 return false; | |
| 1355 } | |
| 1356 return true; | |
| 1357 } | |
| 1358 | |
| 1359 void OverrideConfigLogWriter::OutputMessage(const OutputInfo* output_info) { | |
| 1360 if (log_writer_) { | |
| 1361 log_writer_->OutputMessage(output_info); | |
| 1362 } | |
| 1363 return; | |
| 1364 } | |
| 1365 | |
| 1366 } // namespace omaha | |
| 1367 | |
| 1368 #endif // LOGGING | |
| 1369 | |
| OLD | NEW |