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 // Called upon completion of |WriteDebugLogToFile|. Closes file |
| 32 // descriptor, deletes log file in the case of failure and calls |
| 33 // |callback|. |
| 34 void WriteDebugLogToFileCompleted( |
| 35 const DebugLogWriter::StoreLogsCallback& callback, |
| 36 const base::FilePath& file_path, |
| 37 bool succeeded) { |
| 38 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 39 if (!succeeded) { |
| 40 bool posted = content::BrowserThread::PostBlockingPoolTaskAndReply( |
| 41 FROM_HERE, |
| 42 base::Bind(base::IgnoreResult(&base::DeleteFile), file_path, false), |
| 43 base::Bind(callback, file_path, false)); |
| 44 DCHECK(posted); |
| 45 return; |
| 46 } |
| 47 callback.Run(file_path, true); |
| 48 } |
| 49 |
| 50 // Stores into |file_path| debug logs in the .tgz format. Calls |
| 51 // |callback| upon completion. |
| 52 void WriteDebugLogToFile( |
| 53 base::File* file, |
| 54 const base::FilePath& file_path, |
| 55 bool should_compress, |
| 56 const DebugLogWriter::StoreLogsCallback& callback) { |
| 57 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 58 if (!file->IsValid()) { |
| 59 LOG(ERROR) << |
| 60 "Can't create debug log file: " << file_path.AsUTF8Unsafe() << ", " << |
| 61 "error: " << file->error_details(); |
| 62 return; |
| 63 } |
| 64 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->DumpDebugLogs( |
| 65 should_compress, |
| 66 file->Pass(), |
| 67 base::Bind(&WriteDebugLogToFileCompleted, callback, file_path)); |
| 68 } |
| 69 |
| 70 // Runs command with its parameters as defined in |argv|. |
| 71 // Upon completion, it will report command run outcome via |callback| on the |
| 72 // same thread from where it was initially called from. |
| 73 void RunCommand(const std::vector<std::string>& argv, |
| 74 const CommandCompletionCallback& callback) { |
| 75 base::ProcessHandle handle = base::kNullProcessHandle; |
| 76 if (!base::LaunchProcess(argv, base::LaunchOptions(), &handle)) { |
| 77 LOG(ERROR) << "Failed to execute command " << argv[0]; |
| 78 callback.Run(false); |
| 79 return; |
| 80 } |
| 81 |
| 82 int exit_code = 0; |
| 83 if (!base::WaitForExitCode(handle, &exit_code)) { |
| 84 LOG(ERROR) << "Can't get exit code for pid " << handle; |
| 85 callback.Run(false); |
| 86 return; |
| 87 } |
| 88 callback.Run(exit_code == 0); |
| 89 } |
| 90 |
| 91 // Callback for handling the outcome of CompressArchive(). It reports |
| 92 // the final outcome of log retreival process at via |callback|. |
| 93 void OnCompressArchiveCompleted( |
| 94 const base::FilePath& tar_file_path, |
| 95 const base::FilePath& compressed_output_path, |
| 96 const DebugLogWriter::StoreLogsCallback& callback, |
| 97 bool compression_command_success) { |
| 98 if (!compression_command_success) { |
| 99 LOG(ERROR) << "Failed compressing " << compressed_output_path.value(); |
| 100 content::BrowserThread::PostTask( |
| 101 content::BrowserThread::UI, |
| 102 FROM_HERE, |
| 103 base::Bind(callback, base::FilePath(), false)); |
| 104 base::DeleteFile(tar_file_path, true); |
| 105 base::DeleteFile(compressed_output_path, true); |
| 106 return; |
| 107 } |
| 108 |
| 109 content::BrowserThread::PostTask( |
| 110 content::BrowserThread::UI, |
| 111 FROM_HERE, |
| 112 base::Bind(callback, compressed_output_path, true)); |
| 113 } |
| 114 |
| 115 // Gzips |tar_file_path| and stores results in |compressed_output_path|. |
| 116 void CompressArchive(const base::FilePath& tar_file_path, |
| 117 const base::FilePath& compressed_output_path, |
| 118 const DebugLogWriter::StoreLogsCallback& callback, |
| 119 bool add_user_logs_command_success) { |
| 120 if (!add_user_logs_command_success) { |
| 121 LOG(ERROR) << "Failed adding user logs to " << tar_file_path.value(); |
| 122 content::BrowserThread::PostTask( |
| 123 content::BrowserThread::UI, |
| 124 FROM_HERE, |
| 125 base::Bind(callback, base::FilePath(), false)); |
| 126 base::DeleteFile(tar_file_path, true); |
| 127 return; |
| 128 } |
| 129 |
| 130 std::vector<std::string> argv; |
| 131 argv.push_back(kGzipCommand); |
| 132 argv.push_back(tar_file_path.value()); |
| 133 RunCommand(argv, |
| 134 base::Bind(&OnCompressArchiveCompleted, |
| 135 tar_file_path, |
| 136 compressed_output_path, |
| 137 callback)); |
| 138 } |
| 139 |
| 140 // Adds user sessions specific logs from |user_log_dir| into tar archive file |
| 141 // at |tar_file_path|. Upon completion, it will call CompressArchive() to |
| 142 // produce |compressed_output_path|. |
| 143 void AddUserLogsToArchive(const base::FilePath& user_log_dir, |
| 144 const base::FilePath& tar_file_path, |
| 145 const base::FilePath& compressed_output_path, |
| 146 const DebugLogWriter::StoreLogsCallback& callback) { |
| 147 std::vector<std::string> argv; |
| 148 argv.push_back(kTarCommand); |
| 149 argv.push_back("-rvf"); |
| 150 argv.push_back(tar_file_path.value()); |
| 151 argv.push_back(user_log_dir.value()); |
| 152 RunCommand(argv, |
| 153 base::Bind(&CompressArchive, |
| 154 tar_file_path, |
| 155 compressed_output_path, |
| 156 callback)); |
| 157 } |
| 158 |
| 159 // Appends user logs after system logs are archived into |tar_file_path|. |
| 160 void OnSystemLogsAdded( |
| 161 const DebugLogWriter::StoreLogsCallback& callback, |
| 162 const base::FilePath& tar_file_path, |
| 163 bool succeeded) { |
| 164 if (!succeeded) { |
| 165 callback.Run(base::FilePath(), false); |
| 166 return; |
| 167 } |
| 168 |
| 169 base::FilePath compressed_output_path = |
| 170 tar_file_path.AddExtension(FILE_PATH_LITERAL(".gz")); |
| 171 base::FilePath user_log_dir = |
| 172 logging::GetSessionLogDir(*CommandLine::ForCurrentProcess()); |
| 173 |
| 174 content::BrowserThread::PostBlockingPoolTask( |
| 175 FROM_HERE, |
| 176 base::Bind(&AddUserLogsToArchive, |
| 177 user_log_dir, |
| 178 tar_file_path, |
| 179 compressed_output_path, |
| 180 callback)); |
| 181 } |
| 182 |
| 183 // Starts logs retrieval process. The output will be stored in file with name |
| 184 // derived from |file_name_template|. |
| 185 void StartLogRetrieval(const base::FilePath& file_name_template, |
| 186 bool should_compress, |
| 187 const DebugLogWriter::StoreLogsCallback& callback) { |
| 188 base::FilePath file_path = |
| 189 logging::GenerateTimestampedName(file_name_template, base::Time::Now()); |
| 190 |
| 191 int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE; |
| 192 base::File* file = new base::File; |
| 193 content::BrowserThread::PostBlockingPoolTaskAndReply( |
| 194 FROM_HERE, |
| 195 base::Bind(&base::File::Initialize, |
| 196 base::Unretained(file), file_path, flags), |
| 197 base::Bind(&WriteDebugLogToFile, |
| 198 base::Owned(file), |
| 199 file_path, |
| 200 should_compress, |
| 201 callback)); |
| 202 } |
| 203 |
| 204 } // namespace |
| 205 |
| 206 // static. |
| 207 void DebugLogWriter::StoreLogs( |
| 208 const base::FilePath& fileshelf, |
| 209 bool should_compress, |
| 210 const StoreLogsCallback& callback) { |
| 211 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 212 DCHECK(!callback.is_null()); |
| 213 |
| 214 base::FilePath file_path = fileshelf.Append( |
| 215 should_compress ? FILE_PATH_LITERAL("debug-logs.tgz") : |
| 216 FILE_PATH_LITERAL("debug-logs.tar")); |
| 217 |
| 218 StartLogRetrieval(file_path, should_compress, callback); |
| 219 } |
| 220 |
| 221 // static. |
| 222 void DebugLogWriter::StoreCombinedLogs( |
| 223 const base::FilePath& fileshelf, |
| 224 const StoreLogsCallback& callback) { |
| 225 |
| 226 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 227 DCHECK(!callback.is_null()); |
| 228 |
| 229 base::FilePath file_path = |
| 230 fileshelf.Append(FILE_PATH_LITERAL("combined-logs.tar")); |
| 231 |
| 232 // Get system logs from /var/log first, then add user-specific stuff. |
| 233 StartLogRetrieval(file_path, |
| 234 false, |
| 235 base::Bind(&OnSystemLogsAdded, callback)); |
| 236 } |
| 237 |
| 238 } // namespace chromeos |
OLD | NEW |