diagnostics_log_controller.cc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. // Copyright 2022 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/system/diagnostics/diagnostics_log_controller.h"
  5. #include <memory>
  6. #include <string>
  7. #include <vector>
  8. #include "ash/login_status.h"
  9. #include "ash/session/session_controller_impl.h"
  10. #include "ash/shell.h"
  11. #include "ash/system/diagnostics/diagnostics_browser_delegate.h"
  12. #include "ash/system/diagnostics/networking_log.h"
  13. #include "ash/system/diagnostics/routine_log.h"
  14. #include "ash/system/diagnostics/telemetry_log.h"
  15. #include "base/check_op.h"
  16. #include "base/files/file_path.h"
  17. #include "base/files/file_util.h"
  18. #include "base/sequence_checker.h"
  19. #include "base/strings/string_util.h"
  20. #include "base/task/thread_pool.h"
  21. #include "components/session_manager/session_manager_types.h"
  22. namespace ash {
  23. namespace diagnostics {
  24. namespace {
  25. DiagnosticsLogController* g_instance = nullptr;
  26. // Default path for storing logs.
  27. const char kDiaganosticsTmpDir[] = "/tmp/diagnostics";
  28. const char kDiaganosticsDirName[] = "diagnostics";
  29. // Session log headers and fallback content.
  30. const char kRoutineLogSubsectionHeader[] = "--- Test Routines --- \n";
  31. const char kSystemLogSectionHeader[] = "=== System === \n";
  32. const char kNetworkingLogSectionHeader[] = "=== Networking === \n";
  33. const char kNoRoutinesRun[] =
  34. "No routines of this type were run in the session.\n";
  35. std::string GetRoutineResultsString(const std::string& results) {
  36. const std::string section_header =
  37. std::string(kRoutineLogSubsectionHeader) + "\n";
  38. if (results.empty()) {
  39. return section_header + kNoRoutinesRun;
  40. }
  41. return section_header + results;
  42. }
  43. // Determines if LoginStatus state update should trigger a reset of log
  44. // pointers.
  45. bool ShouldResetAndInitializeLogWritersForLoginStatus(
  46. LoginStatus previous_status,
  47. LoginStatus current_status) {
  48. if (previous_status == current_status)
  49. return false;
  50. switch (current_status) {
  51. case ash::LoginStatus::LOCKED:
  52. // User has not changed.
  53. return false;
  54. case LoginStatus::GUEST:
  55. case LoginStatus::PUBLIC:
  56. case LoginStatus::KIOSK_APP:
  57. case LoginStatus::USER:
  58. case LoginStatus::CHILD:
  59. // Do not reset if user has just unlocked screen.
  60. return previous_status != ash::LoginStatus::LOCKED;
  61. case LoginStatus::NOT_LOGGED_IN:
  62. // When status goes to not_logged_in we should clear existing logs.
  63. return true;
  64. }
  65. }
  66. // Determines if profile should be accessed with current session state. If at
  67. // sign-in screen, guest user, kiosk app, or before the profile has
  68. // successfully loaded temporary path should be used for storing logs.
  69. bool ShouldUseActiveUserProfileDir(session_manager::SessionState state,
  70. LoginStatus status) {
  71. return state == session_manager::SessionState::ACTIVE &&
  72. status == ash::LoginStatus::USER;
  73. }
  74. } // namespace
  75. DiagnosticsLogController::DiagnosticsLogController()
  76. : log_base_path_(kDiaganosticsTmpDir) {
  77. DCHECK_EQ(nullptr, g_instance);
  78. ash::Shell::Get()->session_controller()->AddObserver(this);
  79. g_instance = this;
  80. g_instance->previous_status_ =
  81. ash::Shell::Get()->session_controller()->login_status();
  82. }
  83. DiagnosticsLogController::~DiagnosticsLogController() {
  84. DCHECK_EQ(this, g_instance);
  85. ash::Shell::Get()->session_controller()->RemoveObserver(this);
  86. g_instance = nullptr;
  87. }
  88. // static
  89. DiagnosticsLogController* DiagnosticsLogController::Get() {
  90. return g_instance;
  91. }
  92. // static
  93. bool DiagnosticsLogController::IsInitialized() {
  94. return g_instance && g_instance->delegate_;
  95. }
  96. // static
  97. void DiagnosticsLogController::Initialize(
  98. std::unique_ptr<DiagnosticsBrowserDelegate> delegate) {
  99. DCHECK(g_instance);
  100. DCHECK_CALLED_ON_VALID_SEQUENCE(g_instance->sequence_checker_);
  101. g_instance->delegate_ = std::move(delegate);
  102. g_instance->previous_status_ =
  103. ash::Shell::Get()->session_controller()->login_status();
  104. g_instance->ResetAndInitializeLogWriters();
  105. // Schedule removal of log directory.
  106. base::ThreadPool::PostTask(
  107. FROM_HERE, {base::MayBlock()},
  108. base::BindOnce(&DiagnosticsLogController::RemoveDirectory,
  109. g_instance->weak_ptr_factory_.GetWeakPtr(),
  110. g_instance->log_base_path_));
  111. }
  112. bool DiagnosticsLogController::GenerateSessionLogOnBlockingPool(
  113. const base::FilePath& save_file_path) {
  114. DCHECK(!save_file_path.empty());
  115. std::vector<std::string> log_pieces;
  116. // Fetch system data from TelemetryLog.
  117. const std::string system_log_contents = telemetry_log_->GetContents();
  118. log_pieces.push_back(kSystemLogSectionHeader);
  119. if (!system_log_contents.empty()) {
  120. log_pieces.push_back(system_log_contents);
  121. }
  122. // Fetch system routines from RoutineLog.
  123. const std::string system_routines = routine_log_->GetContentsForCategory(
  124. RoutineLog::RoutineCategory::kSystem);
  125. // Add the routine section for the system category.
  126. log_pieces.push_back(GetRoutineResultsString(system_routines));
  127. if (features::IsNetworkingInDiagnosticsAppEnabled()) {
  128. // Add networking category.
  129. log_pieces.push_back(kNetworkingLogSectionHeader);
  130. // Add the network info section.
  131. log_pieces.push_back(networking_log_->GetNetworkInfo());
  132. // Add the routine section for the network category.
  133. const std::string network_routines = routine_log_->GetContentsForCategory(
  134. RoutineLog::RoutineCategory::kNetwork);
  135. log_pieces.push_back(GetRoutineResultsString(network_routines));
  136. // Add the network events section.
  137. log_pieces.push_back(networking_log_->GetNetworkEvents());
  138. }
  139. return base::WriteFile(save_file_path, base::JoinString(log_pieces, "\n"));
  140. }
  141. void DiagnosticsLogController::ResetAndInitializeLogWriters() {
  142. if (!DiagnosticsLogController::IsInitialized()) {
  143. return;
  144. }
  145. ResetLogBasePath();
  146. networking_log_ = std::make_unique<NetworkingLog>(log_base_path_);
  147. routine_log_ = std::make_unique<RoutineLog>(log_base_path_);
  148. telemetry_log_ = std::make_unique<TelemetryLog>();
  149. }
  150. NetworkingLog* DiagnosticsLogController::GetNetworkingLog() {
  151. return networking_log_.get();
  152. }
  153. RoutineLog* DiagnosticsLogController::GetRoutineLog() {
  154. return routine_log_.get();
  155. }
  156. TelemetryLog* DiagnosticsLogController::GetTelemetryLog() {
  157. return telemetry_log_.get();
  158. }
  159. void DiagnosticsLogController::ResetLogBasePath() {
  160. const session_manager::SessionState state =
  161. ash::Shell::Get()->session_controller()->GetSessionState();
  162. // g_instance->previous_status_ is updated OnLoginStatusChanged after
  163. // ResetLogBasePath. To ensure we have the current status we need to query the
  164. // session_controller for it.
  165. const LoginStatus status =
  166. ash::Shell::Get()->session_controller()->login_status();
  167. // Check if there is an active user and profile is ready based on session and
  168. // login state.
  169. if (ShouldUseActiveUserProfileDir(state, status)) {
  170. base::FilePath user_dir = g_instance->delegate_->GetActiveUserProfileDir();
  171. // Update |log_base_path_| when path is non-empty. Otherwise fallback to
  172. // |kDiaganosticsTmpDir|.
  173. if (!user_dir.empty()) {
  174. g_instance->log_base_path_ = user_dir.Append(kDiaganosticsDirName);
  175. return;
  176. }
  177. }
  178. // Use diagnostics temporary path for Guest, KioskApp, and no user states.
  179. g_instance->log_base_path_ = base::FilePath(kDiaganosticsTmpDir);
  180. }
  181. void DiagnosticsLogController::OnLoginStatusChanged(LoginStatus login_status) {
  182. if (!DiagnosticsLogController::IsInitialized()) {
  183. return;
  184. }
  185. if (ShouldResetAndInitializeLogWritersForLoginStatus(
  186. g_instance->previous_status_, login_status)) {
  187. g_instance->ResetAndInitializeLogWriters();
  188. }
  189. g_instance->previous_status_ = login_status;
  190. }
  191. void DiagnosticsLogController::RemoveDirectory(const base::FilePath& path) {
  192. DCHECK(!path.empty());
  193. if (base::PathExists(path)) {
  194. base::DeletePathRecursively(path);
  195. }
  196. }
  197. } // namespace diagnostics
  198. } // namespace ash