[content shell] implement testRunner.overridePreference
[chromium-blink-merge.git] / chromeos / dbus / debug_daemon_client.cc
blobc9212d31fcf907c34c297397ac359b8436b42f31
1 // Copyright (c) 2012 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 <fcntl.h>
6 #include <unistd.h>
8 #include "chromeos/dbus/debug_daemon_client.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/chromeos/chromeos_version.h"
13 #include "base/memory/ref_counted_memory.h"
14 #include "base/message_loop.h"
15 #include "base/platform_file.h"
16 #include "base/posix/eintr_wrapper.h"
17 #include "base/string_util.h"
18 #include "base/threading/worker_pool.h"
19 #include "dbus/bus.h"
20 #include "dbus/message.h"
21 #include "dbus/object_path.h"
22 #include "dbus/object_proxy.h"
23 #include "net/base/file_stream.h"
24 #include "net/base/io_buffer.h"
25 #include "net/base/net_errors.h"
26 #include "third_party/cros_system_api/dbus/service_constants.h"
28 namespace {
30 // Used in DebugDaemonClient::EmptySystemStopTracingCallback().
31 void EmptyStopSystemTracingCallbackBody(
32 const scoped_refptr<base::RefCountedString>& unused_result) {
35 // Simple class to encapsulate collecting data from a pipe into a
36 // string. To use, instantiate the class, start i/o, and then delete
37 // the instance on callback. The data should be retrieved before
38 // delete and extracted or copied.
40 // TODO(sleffler) move data collection to a sub-class so this
41 // can be reused to process data as it is received
42 class PipeReader {
43 public:
44 typedef base::Callback<void(void)>IOCompleteCallback;
46 explicit PipeReader(IOCompleteCallback callback)
47 : data_stream_(NULL),
48 io_buffer_(new net::IOBufferWithSize(4096)),
49 callback_(callback),
50 weak_ptr_factory_(this) {
51 pipe_fd_[0] = pipe_fd_[1] = -1;
54 virtual ~PipeReader() {
55 // Don't close pipe_fd_[0] as it's closed by data_stream_.
56 if (pipe_fd_[1] != -1)
57 if (HANDLE_EINTR(close(pipe_fd_[1])) < 0)
58 PLOG(ERROR) << "close[1]";
61 // Returns descriptor for the writeable side of the pipe.
62 int GetWriteFD() { return pipe_fd_[1]; }
64 // Closes writeable descriptor; normally used in parent process after fork.
65 void CloseWriteFD() {
66 if (pipe_fd_[1] != -1) {
67 if (HANDLE_EINTR(close(pipe_fd_[1])) < 0)
68 PLOG(ERROR) << "close";
69 pipe_fd_[1] = -1;
73 // Returns collected data.
74 std::string* data() { return &data_; }
76 // Starts data collection. Returns true if stream was setup correctly.
77 // On success data will automatically be accumulated into a string that
78 // can be retrieved with PipeReader::data(). To shutdown collection delete
79 // the instance and/or use PipeReader::OnDataReady(-1).
80 bool StartIO() {
81 // Use a pipe to collect data
82 const int status = HANDLE_EINTR(pipe(pipe_fd_));
83 if (status < 0) {
84 PLOG(ERROR) << "pipe";
85 return false;
87 base::PlatformFile data_file_ = pipe_fd_[0]; // read side
88 data_stream_.reset(new net::FileStream(data_file_,
89 base::PLATFORM_FILE_READ | base::PLATFORM_FILE_ASYNC,
90 NULL));
92 // Post an initial async read to setup data collection
93 int rv = data_stream_->Read(io_buffer_.get(), io_buffer_->size(),
94 base::Bind(&PipeReader::OnDataReady, weak_ptr_factory_.GetWeakPtr()));
95 if (rv != net::ERR_IO_PENDING) {
96 LOG(ERROR) << "Unable to post initial read";
97 return false;
99 return true;
102 // Called when pipe data are available. Can also be used to shutdown
103 // data collection by passing -1 for |byte_count|.
104 void OnDataReady(int byte_count) {
105 DVLOG(1) << "OnDataReady byte_count " << byte_count;
106 if (byte_count <= 0) {
107 callback_.Run(); // signal creator to take data and delete us
108 return;
110 data_.append(io_buffer_->data(), byte_count);
112 // Post another read
113 int rv = data_stream_->Read(io_buffer_.get(), io_buffer_->size(),
114 base::Bind(&PipeReader::OnDataReady, weak_ptr_factory_.GetWeakPtr()));
115 if (rv != net::ERR_IO_PENDING) {
116 LOG(ERROR) << "Unable to post another read";
117 // TODO(sleffler) do something more intelligent?
121 private:
122 friend class base::RefCounted<PipeReader>;
124 int pipe_fd_[2];
125 scoped_ptr<net::FileStream> data_stream_;
126 scoped_refptr<net::IOBufferWithSize> io_buffer_;
127 std::string data_;
128 IOCompleteCallback callback_;
130 // Note: This should remain the last member so it'll be destroyed and
131 // invalidate its weak pointers before any other members are destroyed.
132 base::WeakPtrFactory<PipeReader> weak_ptr_factory_;
134 DISALLOW_COPY_AND_ASSIGN(PipeReader);
137 } // namespace
139 namespace chromeos {
141 // The DebugDaemonClient implementation used in production.
142 class DebugDaemonClientImpl : public DebugDaemonClient {
143 public:
144 explicit DebugDaemonClientImpl(dbus::Bus* bus)
145 : debugdaemon_proxy_(NULL),
146 pipe_reader_(NULL),
147 weak_ptr_factory_(this) {
148 debugdaemon_proxy_ = bus->GetObjectProxy(
149 debugd::kDebugdServiceName,
150 dbus::ObjectPath(debugd::kDebugdServicePath));
153 virtual ~DebugDaemonClientImpl() {}
155 // DebugDaemonClient override.
156 virtual void GetDebugLogs(base::PlatformFile file,
157 const GetDebugLogsCallback& callback) OVERRIDE {
159 dbus::FileDescriptor* file_descriptor = new dbus::FileDescriptor(file);
160 // Punt descriptor validity check to a worker thread; on return we'll
161 // issue the D-Bus request to stop tracing and collect results.
162 base::WorkerPool::PostTaskAndReply(
163 FROM_HERE,
164 base::Bind(&DebugDaemonClientImpl::CheckValidity,
165 file_descriptor),
166 base::Bind(&DebugDaemonClientImpl::OnCheckValidityGetDebugLogs,
167 weak_ptr_factory_.GetWeakPtr(),
168 base::Owned(file_descriptor),
169 callback),
170 false);
173 virtual void SetDebugMode(const std::string& subsystem,
174 const SetDebugModeCallback& callback) OVERRIDE {
175 dbus::MethodCall method_call(debugd::kDebugdInterface,
176 debugd::kSetDebugMode);
177 dbus::MessageWriter writer(&method_call);
178 writer.AppendString(subsystem);
179 debugdaemon_proxy_->CallMethod(
180 &method_call,
181 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
182 base::Bind(&DebugDaemonClientImpl::OnSetDebugMode,
183 weak_ptr_factory_.GetWeakPtr(),
184 callback));
187 virtual void GetRoutes(bool numeric, bool ipv6,
188 const GetRoutesCallback& callback) OVERRIDE {
189 dbus::MethodCall method_call(debugd::kDebugdInterface,
190 debugd::kGetRoutes);
191 dbus::MessageWriter writer(&method_call);
192 dbus::MessageWriter sub_writer(NULL);
193 writer.OpenArray("{sv}", &sub_writer);
194 dbus::MessageWriter elem_writer(NULL);
195 sub_writer.OpenDictEntry(&elem_writer);
196 elem_writer.AppendString("numeric");
197 elem_writer.AppendVariantOfBool(numeric);
198 sub_writer.CloseContainer(&elem_writer);
199 sub_writer.OpenDictEntry(&elem_writer);
200 elem_writer.AppendString("v6");
201 elem_writer.AppendVariantOfBool(ipv6);
202 sub_writer.CloseContainer(&elem_writer);
203 writer.CloseContainer(&sub_writer);
204 debugdaemon_proxy_->CallMethod(
205 &method_call,
206 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
207 base::Bind(&DebugDaemonClientImpl::OnGetRoutes,
208 weak_ptr_factory_.GetWeakPtr(),
209 callback));
212 virtual void GetNetworkStatus(const GetNetworkStatusCallback& callback)
213 OVERRIDE {
214 dbus::MethodCall method_call(debugd::kDebugdInterface,
215 debugd::kGetNetworkStatus);
216 debugdaemon_proxy_->CallMethod(
217 &method_call,
218 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
219 base::Bind(&DebugDaemonClientImpl::OnGetNetworkStatus,
220 weak_ptr_factory_.GetWeakPtr(),
221 callback));
224 virtual void GetModemStatus(const GetModemStatusCallback& callback)
225 OVERRIDE {
226 dbus::MethodCall method_call(debugd::kDebugdInterface,
227 debugd::kGetModemStatus);
228 debugdaemon_proxy_->CallMethod(
229 &method_call,
230 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
231 base::Bind(&DebugDaemonClientImpl::OnGetModemStatus,
232 weak_ptr_factory_.GetWeakPtr(),
233 callback));
236 virtual void GetNetworkInterfaces(
237 const GetNetworkInterfacesCallback& callback) OVERRIDE {
238 dbus::MethodCall method_call(debugd::kDebugdInterface,
239 debugd::kGetInterfaces);
240 debugdaemon_proxy_->CallMethod(
241 &method_call,
242 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
243 base::Bind(&DebugDaemonClientImpl::OnGetNetworkInterfaces,
244 weak_ptr_factory_.GetWeakPtr(),
245 callback));
248 virtual void GetAllLogs(const GetLogsCallback& callback)
249 OVERRIDE {
250 dbus::MethodCall method_call(debugd::kDebugdInterface,
251 debugd::kGetAllLogs);
252 debugdaemon_proxy_->CallMethod(
253 &method_call,
254 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
255 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs,
256 weak_ptr_factory_.GetWeakPtr(),
257 callback));
260 virtual void GetUserLogFiles(
261 const GetLogsCallback& callback) OVERRIDE {
262 dbus::MethodCall method_call(debugd::kDebugdInterface,
263 debugd::kGetUserLogFiles);
264 debugdaemon_proxy_->CallMethod(
265 &method_call,
266 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
267 base::Bind(&DebugDaemonClientImpl::OnGetUserLogFiles,
268 weak_ptr_factory_.GetWeakPtr(),
269 callback));
272 virtual void StartSystemTracing() OVERRIDE {
273 dbus::MethodCall method_call(
274 debugd::kDebugdInterface,
275 debugd::kSystraceStart);
276 dbus::MessageWriter writer(&method_call);
277 writer.AppendString("all"); // TODO(sleffler) parameterize category list
279 DVLOG(1) << "Requesting a systrace start";
280 debugdaemon_proxy_->CallMethod(
281 &method_call,
282 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
283 base::Bind(&DebugDaemonClientImpl::OnStartSystemTracing,
284 weak_ptr_factory_.GetWeakPtr()));
287 virtual bool RequestStopSystemTracing(const StopSystemTracingCallback&
288 callback) OVERRIDE {
289 if (pipe_reader_ != NULL) {
290 LOG(ERROR) << "Busy doing StopSystemTracing";
291 return false;
294 pipe_reader_.reset(new PipeReader(
295 base::Bind(&DebugDaemonClientImpl::OnIOComplete,
296 weak_ptr_factory_.GetWeakPtr())));
297 int write_fd = -1;
298 if (!pipe_reader_->StartIO()) {
299 LOG(ERROR) << "Cannot create pipe reader";
300 // NB: continue anyway to shutdown tracing; toss trace data
301 write_fd = HANDLE_EINTR(open("/dev/null", O_WRONLY));
302 // TODO(sleffler) if this fails AppendFileDescriptor will abort
303 } else {
304 write_fd = pipe_reader_->GetWriteFD();
307 dbus::FileDescriptor* file_descriptor = new dbus::FileDescriptor(write_fd);
308 // Punt descriptor validity check to a worker thread; on return we'll
309 // issue the D-Bus request to stop tracing and collect results.
310 base::WorkerPool::PostTaskAndReply(
311 FROM_HERE,
312 base::Bind(&DebugDaemonClientImpl::CheckValidity,
313 file_descriptor),
314 base::Bind(&DebugDaemonClientImpl::OnCheckValidityRequestStopSystem,
315 weak_ptr_factory_.GetWeakPtr(),
316 base::Owned(file_descriptor),
317 callback),
318 false);
320 return true;
323 virtual void TestICMP(const std::string& ip_address,
324 const TestICMPCallback& callback) OVERRIDE {
325 dbus::MethodCall method_call(debugd::kDebugdInterface,
326 debugd::kTestICMP);
327 dbus::MessageWriter writer(&method_call);
328 writer.AppendString(ip_address);
329 debugdaemon_proxy_->CallMethod(
330 &method_call,
331 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
332 base::Bind(&DebugDaemonClientImpl::OnTestICMP,
333 weak_ptr_factory_.GetWeakPtr(),
334 callback));
337 private:
338 // Called to check descriptor validity on a thread where i/o is permitted.
339 static void CheckValidity(dbus::FileDescriptor* file_descriptor) {
340 file_descriptor->CheckValidity();
343 // Called when a CheckValidity response is received.
344 void OnCheckValidityGetDebugLogs(dbus::FileDescriptor* file_descriptor,
345 const GetDebugLogsCallback& callback) {
346 // Issue the dbus request to get debug logs.
347 dbus::MethodCall method_call(
348 debugd::kDebugdInterface,
349 debugd::kGetDebugLogs);
350 dbus::MessageWriter writer(&method_call);
351 writer.AppendFileDescriptor(*file_descriptor);
353 debugdaemon_proxy_->CallMethod(
354 &method_call,
355 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
356 base::Bind(&DebugDaemonClientImpl::OnGetDebugLogs,
357 weak_ptr_factory_.GetWeakPtr(),
358 callback));
361 // Called when a response for GetDebugLogs() is received.
362 void OnGetDebugLogs(const GetDebugLogsCallback& callback,
363 dbus::Response* response) {
364 if (!response) {
365 LOG(ERROR) << "Failed to get debug logs";
366 callback.Run(false);
367 return;
369 callback.Run(true);
372 // Called when a response for SetDebugMode() is received.
373 void OnSetDebugMode(const SetDebugModeCallback& callback,
374 dbus::Response* response) {
375 if (!response) {
376 LOG(ERROR) << "Failed to change debug mode";
377 callback.Run(false);
378 } else {
379 callback.Run(true);
383 void OnGetRoutes(const GetRoutesCallback& callback,
384 dbus::Response* response) {
385 std::vector<std::string> routes;
386 if (response) {
387 dbus::MessageReader reader(response);
388 if (reader.PopArrayOfStrings(&routes)) {
389 callback.Run(true, routes);
390 } else {
391 LOG(ERROR) << "Got non-array response from GetRoutes";
392 callback.Run(false, routes);
394 } else {
395 callback.Run(false, routes);
399 void OnGetNetworkStatus(const GetNetworkStatusCallback& callback,
400 dbus::Response* response) {
401 std::string status;
402 if (response && dbus::MessageReader(response).PopString(&status))
403 callback.Run(true, status);
404 else
405 callback.Run(false, "");
408 void OnGetModemStatus(const GetModemStatusCallback& callback,
409 dbus::Response* response) {
410 std::string status;
411 if (response && dbus::MessageReader(response).PopString(&status))
412 callback.Run(true, status);
413 else
414 callback.Run(false, "");
417 void OnGetNetworkInterfaces(const GetNetworkInterfacesCallback& callback,
418 dbus::Response* response) {
419 std::string status;
420 if (response && dbus::MessageReader(response).PopString(&status))
421 callback.Run(true, status);
422 else
423 callback.Run(false, "");
426 void OnGetAllLogs(const GetLogsCallback& callback,
427 dbus::Response* response) {
428 std::map<std::string, std::string> logs;
429 bool broken = false; // did we see a broken (k,v) pair?
430 dbus::MessageReader sub_reader(NULL);
431 if (!response || !dbus::MessageReader(response).PopArray(&sub_reader)) {
432 callback.Run(false, logs);
433 return;
435 while (sub_reader.HasMoreData()) {
436 dbus::MessageReader sub_sub_reader(NULL);
437 std::string key, value;
438 if (!sub_reader.PopDictEntry(&sub_sub_reader)
439 || !sub_sub_reader.PopString(&key)
440 || !sub_sub_reader.PopString(&value)) {
441 broken = true;
442 break;
444 logs[key] = value;
446 callback.Run(!sub_reader.HasMoreData() && !broken, logs);
449 void OnGetUserLogFiles(const GetLogsCallback& callback,
450 dbus::Response* response) {
451 return OnGetAllLogs(callback, response);
454 // Called when a response for StartSystemTracing() is received.
455 void OnStartSystemTracing(dbus::Response* response) {
456 if (!response) {
457 LOG(ERROR) << "Failed to request systrace start";
458 return;
462 // Called when a CheckValidity response is received.
463 void OnCheckValidityRequestStopSystem(
464 dbus::FileDescriptor* file_descriptor,
465 const StopSystemTracingCallback& callback) {
466 // Issue the dbus request to stop system tracing
467 dbus::MethodCall method_call(
468 debugd::kDebugdInterface,
469 debugd::kSystraceStop);
470 dbus::MessageWriter writer(&method_call);
471 writer.AppendFileDescriptor(*file_descriptor);
473 callback_ = callback;
475 DVLOG(1) << "Requesting a systrace stop";
476 debugdaemon_proxy_->CallMethod(
477 &method_call,
478 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
479 base::Bind(&DebugDaemonClientImpl::OnRequestStopSystemTracing,
480 weak_ptr_factory_.GetWeakPtr()));
482 pipe_reader_->CloseWriteFD(); // close our copy of fd after send
485 // Called when a response for RequestStopSystemTracing() is received.
486 void OnRequestStopSystemTracing(dbus::Response* response) {
487 if (!response) {
488 LOG(ERROR) << "Failed to request systrace stop";
489 // If debugd crashes or completes I/O before this message is processed
490 // then pipe_reader_ can be NULL, see OnIOComplete().
491 if (pipe_reader_.get())
492 pipe_reader_->OnDataReady(-1); // terminate data stream
494 // NB: requester is signaled when i/o completes
497 void OnTestICMP(const TestICMPCallback& callback, dbus::Response* response) {
498 std::string status;
499 if (response && dbus::MessageReader(response).PopString(&status))
500 callback.Run(true, status);
501 else
502 callback.Run(false, "");
505 // Called when pipe i/o completes; pass data on and delete the instance.
506 void OnIOComplete() {
507 callback_.Run(base::RefCountedString::TakeString(pipe_reader_->data()));
508 pipe_reader_.reset();
511 dbus::ObjectProxy* debugdaemon_proxy_;
512 scoped_ptr<PipeReader> pipe_reader_;
513 StopSystemTracingCallback callback_;
514 base::WeakPtrFactory<DebugDaemonClientImpl> weak_ptr_factory_;
516 DISALLOW_COPY_AND_ASSIGN(DebugDaemonClientImpl);
519 // The DebugDaemonClient implementation used on Linux desktop,
520 // which does nothing.
521 class DebugDaemonClientStubImpl : public DebugDaemonClient {
522 // DebugDaemonClient overrides.
523 virtual void GetDebugLogs(base::PlatformFile file,
524 const GetDebugLogsCallback& callback) OVERRIDE {
525 callback.Run(false);
527 virtual void SetDebugMode(const std::string& subsystem,
528 const SetDebugModeCallback& callback) OVERRIDE {
529 callback.Run(false);
531 virtual void StartSystemTracing() OVERRIDE {}
532 virtual bool RequestStopSystemTracing(const StopSystemTracingCallback&
533 callback) OVERRIDE {
534 std::string no_data;
535 callback.Run(base::RefCountedString::TakeString(&no_data));
536 return true;
538 virtual void GetRoutes(bool numeric, bool ipv6,
539 const GetRoutesCallback& callback) OVERRIDE {
540 std::vector<std::string> empty;
541 MessageLoop::current()->PostTask(FROM_HERE,
542 base::Bind(callback, false, empty));
544 virtual void GetNetworkStatus(const GetNetworkStatusCallback& callback)
545 OVERRIDE {
546 MessageLoop::current()->PostTask(FROM_HERE,
547 base::Bind(callback, false, ""));
549 virtual void GetModemStatus(const GetModemStatusCallback& callback)
550 OVERRIDE {
551 MessageLoop::current()->PostTask(FROM_HERE,
552 base::Bind(callback, false, ""));
554 virtual void GetNetworkInterfaces(
555 const GetNetworkInterfacesCallback& callback) OVERRIDE {
556 MessageLoop::current()->PostTask(FROM_HERE,
557 base::Bind(callback, false, ""));
559 virtual void GetAllLogs(const GetLogsCallback& callback) OVERRIDE {
560 std::map<std::string, std::string> empty;
561 MessageLoop::current()->PostTask(FROM_HERE,
562 base::Bind(callback, false, empty));
564 virtual void GetUserLogFiles(const GetLogsCallback& callback) OVERRIDE {
565 std::map<std::string, std::string> empty;
566 MessageLoop::current()->PostTask(FROM_HERE,
567 base::Bind(callback, false, empty));
570 virtual void TestICMP(const std::string& ip_address,
571 const TestICMPCallback& callback) OVERRIDE {
572 MessageLoop::current()->PostTask(FROM_HERE,
573 base::Bind(callback, false, ""));
577 DebugDaemonClient::DebugDaemonClient() {
580 DebugDaemonClient::~DebugDaemonClient() {
583 // static
584 DebugDaemonClient::StopSystemTracingCallback
585 DebugDaemonClient::EmptyStopSystemTracingCallback() {
586 return base::Bind(&EmptyStopSystemTracingCallbackBody);
589 // static
590 DebugDaemonClient* DebugDaemonClient::Create(DBusClientImplementationType type,
591 dbus::Bus* bus) {
592 if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
593 return new DebugDaemonClientImpl(bus);
594 DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
595 return new DebugDaemonClientStubImpl();
598 } // namespace chromeos