OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 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 |
| 5 #include "chrome/browser/chromeos/system_logs/debug_log_writer.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/bind_helpers.h" |
| 9 #include "base/callback.h" |
| 10 #include "base/command_line.h" |
| 11 #include "base/file_util.h" |
| 12 #include "base/files/file.h" |
| 13 #include "base/message_loop/message_loop.h" |
| 14 #include "base/process/kill.h" |
| 15 #include "base/process/launch.h" |
| 16 #include "chrome/common/logging_chrome.h" |
| 17 #include "chromeos/dbus/dbus_thread_manager.h" |
| 18 #include "chromeos/dbus/debug_daemon_client.h" |
| 19 #include "content/public/browser/browser_thread.h" |
| 20 |
| 21 namespace chromeos { |
| 22 |
| 23 namespace { |
| 24 |
| 25 // Callback for returning status of executed external command. |
| 26 typedef base::Callback<void(bool succeeded)> CommandCompletionCallback; |
| 27 |
| 28 const char kGzipCommand[] = "/bin/gzip"; |
| 29 const char kTarCommand[] = "/bin/tar"; |
| 30 |
| 31 scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner( |
| 32 const std::string sequence_name) { |
| 33 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); |
| 34 return pool->GetSequencedTaskRunnerWithShutdownBehavior( |
| 35 pool->GetNamedSequenceToken(sequence_name), |
| 36 base::SequencedWorkerPool::BLOCK_SHUTDOWN); |
| 37 } |
| 38 |
| 39 // Called upon completion of |WriteDebugLogToFile|. Closes file |
| 40 // descriptor, deletes log file in the case of failure and calls |
| 41 // |callback|. |
| 42 void WriteDebugLogToFileCompleted( |
| 43 const base::FilePath& file_path, |
| 44 const std::string& sequence_token_name, |
| 45 const DebugLogWriter::StoreLogsCallback& callback, |
| 46 bool succeeded) { |
| 47 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 48 if (!succeeded) { |
| 49 bool posted = GetSequencedTaskRunner(sequence_token_name)->PostTaskAndReply( |
| 50 FROM_HERE, |
| 51 base::Bind(base::IgnoreResult(&base::DeleteFile), file_path, false), |
| 52 base::Bind(callback, file_path, false)); |
| 53 DCHECK(posted); |
| 54 return; |
| 55 } |
| 56 if (!callback.is_null()) |
| 57 callback.Run(file_path, true); |
| 58 } |
| 59 |
| 60 // Stores into |file_path| debug logs in the .tgz format. Calls |
| 61 // |callback| upon completion. |
| 62 void WriteDebugLogToFile(base::File* file, |
| 63 const std::string& sequence_token_name, |
| 64 const base::FilePath& file_path, |
| 65 bool should_compress, |
| 66 const DebugLogWriter::StoreLogsCallback& callback) { |
| 67 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 68 if (!file->IsValid()) { |
| 69 LOG(ERROR) << "Can't create debug log file: " << file_path.AsUTF8Unsafe() |
| 70 << ", " |
| 71 << "error: " << file->error_details(); |
| 72 return; |
| 73 } |
| 74 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->DumpDebugLogs( |
| 75 should_compress, |
| 76 file->Pass(), |
| 77 base::Bind(&WriteDebugLogToFileCompleted, |
| 78 file_path, |
| 79 sequence_token_name, |
| 80 callback)); |
| 81 } |
| 82 |
| 83 // Runs command with its parameters as defined in |argv|. |
| 84 // Upon completion, it will report command run outcome via |callback| on the |
| 85 // same thread from where it was initially called from. |
| 86 void RunCommand(const std::vector<std::string>& argv, |
| 87 const CommandCompletionCallback& callback) { |
| 88 base::ProcessHandle handle = base::kNullProcessHandle; |
| 89 if (!base::LaunchProcess(argv, base::LaunchOptions(), &handle)) { |
| 90 LOG(ERROR) << "Failed to execute command " << argv[0]; |
| 91 if (!callback.is_null()) |
| 92 callback.Run(false); |
| 93 |
| 94 return; |
| 95 } |
| 96 |
| 97 int exit_code = 0; |
| 98 if (!base::WaitForExitCode(handle, &exit_code)) { |
| 99 LOG(ERROR) << "Can't get exit code for pid " << handle; |
| 100 if (!callback.is_null()) |
| 101 callback.Run(false); |
| 102 |
| 103 return; |
| 104 } |
| 105 if (!callback.is_null()) |
| 106 callback.Run(exit_code == 0); |
| 107 } |
| 108 |
| 109 // Callback for handling the outcome of CompressArchive(). It reports |
| 110 // the final outcome of log retreival process at via |callback|. |
| 111 void OnCompressArchiveCompleted( |
| 112 const base::FilePath& tar_file_path, |
| 113 const base::FilePath& compressed_output_path, |
| 114 const DebugLogWriter::StoreLogsCallback& callback, |
| 115 bool compression_command_success) { |
| 116 if (!compression_command_success) { |
| 117 LOG(ERROR) << "Failed compressing " << compressed_output_path.value(); |
| 118 content::BrowserThread::PostTask( |
| 119 content::BrowserThread::UI, |
| 120 FROM_HERE, |
| 121 base::Bind(callback, base::FilePath(), false)); |
| 122 base::DeleteFile(tar_file_path, true); |
| 123 base::DeleteFile(compressed_output_path, true); |
| 124 return; |
| 125 } |
| 126 |
| 127 content::BrowserThread::PostTask( |
| 128 content::BrowserThread::UI, |
| 129 FROM_HERE, |
| 130 base::Bind(callback, compressed_output_path, true)); |
| 131 } |
| 132 |
| 133 // Gzips |tar_file_path| and stores results in |compressed_output_path|. |
| 134 void CompressArchive(const base::FilePath& tar_file_path, |
| 135 const base::FilePath& compressed_output_path, |
| 136 const DebugLogWriter::StoreLogsCallback& callback, |
| 137 bool add_user_logs_command_success) { |
| 138 if (!add_user_logs_command_success) { |
| 139 LOG(ERROR) << "Failed adding user logs to " << tar_file_path.value(); |
| 140 content::BrowserThread::PostTask( |
| 141 content::BrowserThread::UI, |
| 142 FROM_HERE, |
| 143 base::Bind(callback, base::FilePath(), false)); |
| 144 base::DeleteFile(tar_file_path, true); |
| 145 return; |
| 146 } |
| 147 |
| 148 std::vector<std::string> argv; |
| 149 argv.push_back(kGzipCommand); |
| 150 argv.push_back(tar_file_path.value()); |
| 151 RunCommand(argv, |
| 152 base::Bind(&OnCompressArchiveCompleted, |
| 153 tar_file_path, |
| 154 compressed_output_path, |
| 155 callback)); |
| 156 } |
| 157 |
| 158 // Adds user sessions specific logs from |user_log_dir| into tar archive file |
| 159 // at |tar_file_path|. Upon completion, it will call CompressArchive() to |
| 160 // produce |compressed_output_path|. |
| 161 void AddUserLogsToArchive(const base::FilePath& user_log_dir, |
| 162 const base::FilePath& tar_file_path, |
| 163 const base::FilePath& compressed_output_path, |
| 164 const DebugLogWriter::StoreLogsCallback& callback) { |
| 165 std::vector<std::string> argv; |
| 166 argv.push_back(kTarCommand); |
| 167 argv.push_back("-rvf"); |
| 168 argv.push_back(tar_file_path.value()); |
| 169 argv.push_back(user_log_dir.value()); |
| 170 RunCommand( |
| 171 argv, |
| 172 base::Bind( |
| 173 &CompressArchive, tar_file_path, compressed_output_path, callback)); |
| 174 } |
| 175 |
| 176 // Appends user logs after system logs are archived into |tar_file_path|. |
| 177 void OnSystemLogsAdded(const DebugLogWriter::StoreLogsCallback& callback, |
| 178 const base::FilePath& tar_file_path, |
| 179 bool succeeded) { |
| 180 if (!succeeded) { |
| 181 if (!callback.is_null()) |
| 182 callback.Run(base::FilePath(), false); |
| 183 |
| 184 return; |
| 185 } |
| 186 |
| 187 base::FilePath compressed_output_path = |
| 188 tar_file_path.AddExtension(FILE_PATH_LITERAL(".gz")); |
| 189 base::FilePath user_log_dir = |
| 190 logging::GetSessionLogDir(*CommandLine::ForCurrentProcess()); |
| 191 |
| 192 content::BrowserThread::PostBlockingPoolTask( |
| 193 FROM_HERE, |
| 194 base::Bind(&AddUserLogsToArchive, |
| 195 user_log_dir, |
| 196 tar_file_path, |
| 197 compressed_output_path, |
| 198 callback)); |
| 199 } |
| 200 |
| 201 void IntializeLogFile(base::File* file, |
| 202 const base::FilePath& file_path, |
| 203 uint32 flags) { |
| 204 base::FilePath dir = file_path.DirName(); |
| 205 if (!base::DirectoryExists(dir)) { |
| 206 if (!base::CreateDirectory(dir)) { |
| 207 LOG(ERROR) << "Can not create " << dir.value(); |
| 208 return; |
| 209 } |
| 210 } |
| 211 |
| 212 file->Initialize(file_path, flags); |
| 213 } |
| 214 |
| 215 // Starts logs retrieval process. The output will be stored in file with name |
| 216 // derived from |file_name_template|. |
| 217 void StartLogRetrieval(const base::FilePath& file_name_template, |
| 218 bool should_compress, |
| 219 const std::string& sequence_token_name, |
| 220 const DebugLogWriter::StoreLogsCallback& callback) { |
| 221 base::FilePath file_path = |
| 222 logging::GenerateTimestampedName(file_name_template, base::Time::Now()); |
| 223 |
| 224 int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE; |
| 225 base::File* file = new base::File; |
| 226 GetSequencedTaskRunner(sequence_token_name)->PostTaskAndReply( |
| 227 FROM_HERE, |
| 228 base::Bind(&IntializeLogFile, base::Unretained(file), file_path, flags), |
| 229 base::Bind(&WriteDebugLogToFile, |
| 230 base::Owned(file), |
| 231 sequence_token_name, |
| 232 file_path, |
| 233 should_compress, |
| 234 callback)); |
| 235 } |
| 236 |
| 237 const char kDefaultSequenceName[] = "DebugLogWriter"; |
| 238 |
| 239 } // namespace |
| 240 |
| 241 // static. |
| 242 void DebugLogWriter::StoreLogs(const base::FilePath& fileshelf, |
| 243 bool should_compress, |
| 244 const StoreLogsCallback& callback) { |
| 245 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 246 DCHECK(!callback.is_null()); |
| 247 |
| 248 base::FilePath file_path = |
| 249 fileshelf.Append(should_compress ? FILE_PATH_LITERAL("debug-logs.tgz") |
| 250 : FILE_PATH_LITERAL("debug-logs.tar")); |
| 251 |
| 252 StartLogRetrieval(file_path, should_compress, kDefaultSequenceName, callback); |
| 253 } |
| 254 |
| 255 // static. |
| 256 void DebugLogWriter::StoreCombinedLogs(const base::FilePath& fileshelf, |
| 257 const std::string& sequence_token_name, |
| 258 const StoreLogsCallback& callback) { |
| 259 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 260 DCHECK(!callback.is_null()); |
| 261 |
| 262 base::FilePath file_path = |
| 263 fileshelf.Append(FILE_PATH_LITERAL("combined-logs.tar")); |
| 264 |
| 265 // Get system logs from /var/log first, then add user-specific stuff. |
| 266 StartLogRetrieval(file_path, |
| 267 false, |
| 268 sequence_token_name, |
| 269 base::Bind(&OnSystemLogsAdded, callback)); |
| 270 } |
| 271 |
| 272 } // namespace chromeos |
OLD | NEW |