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.
5 #include "chrome/browser/chromeos/system_logs/debug_log_writer.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/command_line.h"
11 #include "base/files/file.h"
12 #include "base/files/file_util.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"
25 // Callback for returning status of executed external command.
26 typedef base::Callback
<void(bool succeeded
)> CommandCompletionCallback
;
28 const char kGzipCommand
[] = "/bin/gzip";
29 const char kTarCommand
[] = "/bin/tar";
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
);
39 // Called upon completion of |WriteDebugLogToFile|. Closes file
40 // descriptor, deletes log file in the case of failure and calls
42 void WriteDebugLogToFileCompleted(
43 const base::FilePath
& file_path
,
44 const std::string
& sequence_token_name
,
45 const DebugLogWriter::StoreLogsCallback
& callback
,
47 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
49 bool posted
= GetSequencedTaskRunner(sequence_token_name
)->PostTaskAndReply(
51 base::Bind(base::IgnoreResult(&base::DeleteFile
), file_path
, false),
52 base::Bind(callback
, file_path
, false));
56 if (!callback
.is_null())
57 callback
.Run(file_path
, true);
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
,
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()
71 << "error: " << file
->error_details();
74 scoped_refptr
<base::TaskRunner
> task_runner
=
75 GetSequencedTaskRunner(sequence_token_name
);
76 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->DumpDebugLogs(
80 base::Bind(&WriteDebugLogToFileCompleted
,
86 // Runs command with its parameters as defined in |argv|.
87 // Upon completion, it will report command run outcome via |callback| on the
88 // same thread from where it was initially called from.
89 void RunCommand(const std::vector
<std::string
>& argv
,
90 const CommandCompletionCallback
& callback
) {
91 base::Process process
= base::LaunchProcess(argv
, base::LaunchOptions());
92 if (!process
.IsValid()) {
93 LOG(ERROR
) << "Failed to execute command " << argv
[0];
94 if (!callback
.is_null())
101 if (!process
.WaitForExit(&exit_code
)) {
102 LOG(ERROR
) << "Can't get exit code for pid " << process
.Pid();
103 if (!callback
.is_null())
108 if (!callback
.is_null())
109 callback
.Run(exit_code
== 0);
112 // Callback for handling the outcome of CompressArchive(). It reports
113 // the final outcome of log retreival process at via |callback|.
114 void OnCompressArchiveCompleted(
115 const base::FilePath
& tar_file_path
,
116 const base::FilePath
& compressed_output_path
,
117 const DebugLogWriter::StoreLogsCallback
& callback
,
118 bool compression_command_success
) {
119 if (!compression_command_success
) {
120 LOG(ERROR
) << "Failed compressing " << compressed_output_path
.value();
121 content::BrowserThread::PostTask(
122 content::BrowserThread::UI
,
124 base::Bind(callback
, base::FilePath(), false));
125 base::DeleteFile(tar_file_path
, true);
126 base::DeleteFile(compressed_output_path
, true);
130 content::BrowserThread::PostTask(
131 content::BrowserThread::UI
,
133 base::Bind(callback
, compressed_output_path
, true));
136 // Gzips |tar_file_path| and stores results in |compressed_output_path|.
137 void CompressArchive(const base::FilePath
& tar_file_path
,
138 const base::FilePath
& compressed_output_path
,
139 const DebugLogWriter::StoreLogsCallback
& callback
,
140 bool add_user_logs_command_success
) {
141 if (!add_user_logs_command_success
) {
142 LOG(ERROR
) << "Failed adding user logs to " << tar_file_path
.value();
143 content::BrowserThread::PostTask(
144 content::BrowserThread::UI
,
146 base::Bind(callback
, base::FilePath(), false));
147 base::DeleteFile(tar_file_path
, true);
151 std::vector
<std::string
> argv
;
152 argv
.push_back(kGzipCommand
);
153 argv
.push_back(tar_file_path
.value());
155 base::Bind(&OnCompressArchiveCompleted
,
157 compressed_output_path
,
161 // Adds user sessions specific logs from |user_log_dir| into tar archive file
162 // at |tar_file_path|. Upon completion, it will call CompressArchive() to
163 // produce |compressed_output_path|.
164 void AddUserLogsToArchive(const base::FilePath
& user_log_dir
,
165 const base::FilePath
& tar_file_path
,
166 const base::FilePath
& compressed_output_path
,
167 const DebugLogWriter::StoreLogsCallback
& callback
) {
168 std::vector
<std::string
> argv
;
169 argv
.push_back(kTarCommand
);
170 argv
.push_back("-rvf");
171 argv
.push_back(tar_file_path
.value());
172 argv
.push_back(user_log_dir
.value());
176 &CompressArchive
, tar_file_path
, compressed_output_path
, callback
));
179 // Appends user logs after system logs are archived into |tar_file_path|.
180 void OnSystemLogsAdded(const DebugLogWriter::StoreLogsCallback
& callback
,
181 const base::FilePath
& tar_file_path
,
184 if (!callback
.is_null())
185 callback
.Run(base::FilePath(), false);
190 base::FilePath compressed_output_path
=
191 tar_file_path
.AddExtension(FILE_PATH_LITERAL(".gz"));
192 base::FilePath user_log_dir
=
193 logging::GetSessionLogDir(*base::CommandLine::ForCurrentProcess());
195 content::BrowserThread::PostBlockingPoolTask(
197 base::Bind(&AddUserLogsToArchive
,
200 compressed_output_path
,
204 void IntializeLogFile(base::File
* file
,
205 const base::FilePath
& file_path
,
207 base::FilePath dir
= file_path
.DirName();
208 if (!base::DirectoryExists(dir
)) {
209 if (!base::CreateDirectory(dir
)) {
210 LOG(ERROR
) << "Can not create " << dir
.value();
215 file
->Initialize(file_path
, flags
);
218 // Starts logs retrieval process. The output will be stored in file with name
219 // derived from |file_name_template|.
220 void StartLogRetrieval(const base::FilePath
& file_name_template
,
221 bool should_compress
,
222 const std::string
& sequence_token_name
,
223 const DebugLogWriter::StoreLogsCallback
& callback
) {
224 base::FilePath file_path
=
225 logging::GenerateTimestampedName(file_name_template
, base::Time::Now());
227 int flags
= base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
;
228 base::File
* file
= new base::File
;
229 GetSequencedTaskRunner(sequence_token_name
)->PostTaskAndReply(
231 base::Bind(&IntializeLogFile
, base::Unretained(file
), file_path
, flags
),
232 base::Bind(&WriteDebugLogToFile
,
240 const char kDefaultSequenceName
[] = "DebugLogWriter";
245 void DebugLogWriter::StoreLogs(const base::FilePath
& fileshelf
,
246 bool should_compress
,
247 const StoreLogsCallback
& callback
) {
248 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
249 DCHECK(!callback
.is_null());
251 base::FilePath file_path
=
252 fileshelf
.Append(should_compress
? FILE_PATH_LITERAL("debug-logs.tgz")
253 : FILE_PATH_LITERAL("debug-logs.tar"));
255 StartLogRetrieval(file_path
, should_compress
, kDefaultSequenceName
, callback
);
259 void DebugLogWriter::StoreCombinedLogs(const base::FilePath
& fileshelf
,
260 const std::string
& sequence_token_name
,
261 const StoreLogsCallback
& callback
) {
262 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
263 DCHECK(!callback
.is_null());
265 base::FilePath file_path
=
266 fileshelf
.Append(FILE_PATH_LITERAL("combined-logs.tar"));
268 // Get system logs from /var/log first, then add user-specific stuff.
269 StartLogRetrieval(file_path
,
272 base::Bind(&OnSystemLogsAdded
, callback
));
275 } // namespace chromeos