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.
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"
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"
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
44 typedef base::Callback
<void(void)>IOCompleteCallback
;
46 explicit PipeReader(IOCompleteCallback callback
)
48 io_buffer_(new net::IOBufferWithSize(4096)),
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.
66 if (pipe_fd_
[1] != -1) {
67 if (HANDLE_EINTR(close(pipe_fd_
[1])) < 0)
68 PLOG(ERROR
) << "close";
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).
81 // Use a pipe to collect data
82 const int status
= HANDLE_EINTR(pipe(pipe_fd_
));
84 PLOG(ERROR
) << "pipe";
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
,
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";
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
110 data_
.append(io_buffer_
->data(), byte_count
);
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?
122 friend class base::RefCounted
<PipeReader
>;
125 scoped_ptr
<net::FileStream
> data_stream_
;
126 scoped_refptr
<net::IOBufferWithSize
> io_buffer_
;
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
);
141 // The DebugDaemonClient implementation used in production.
142 class DebugDaemonClientImpl
: public DebugDaemonClient
{
144 explicit DebugDaemonClientImpl(dbus::Bus
* bus
)
145 : debugdaemon_proxy_(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(
164 base::Bind(&DebugDaemonClientImpl::CheckValidity
,
166 base::Bind(&DebugDaemonClientImpl::OnCheckValidityGetDebugLogs
,
167 weak_ptr_factory_
.GetWeakPtr(),
168 base::Owned(file_descriptor
),
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(
181 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
182 base::Bind(&DebugDaemonClientImpl::OnSetDebugMode
,
183 weak_ptr_factory_
.GetWeakPtr(),
187 virtual void GetRoutes(bool numeric
, bool ipv6
,
188 const GetRoutesCallback
& callback
) OVERRIDE
{
189 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
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(
206 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
207 base::Bind(&DebugDaemonClientImpl::OnGetRoutes
,
208 weak_ptr_factory_
.GetWeakPtr(),
212 virtual void GetNetworkStatus(const GetNetworkStatusCallback
& callback
)
214 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
215 debugd::kGetNetworkStatus
);
216 debugdaemon_proxy_
->CallMethod(
218 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
219 base::Bind(&DebugDaemonClientImpl::OnGetNetworkStatus
,
220 weak_ptr_factory_
.GetWeakPtr(),
224 virtual void GetModemStatus(const GetModemStatusCallback
& callback
)
226 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
227 debugd::kGetModemStatus
);
228 debugdaemon_proxy_
->CallMethod(
230 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
231 base::Bind(&DebugDaemonClientImpl::OnGetModemStatus
,
232 weak_ptr_factory_
.GetWeakPtr(),
236 virtual void GetNetworkInterfaces(
237 const GetNetworkInterfacesCallback
& callback
) OVERRIDE
{
238 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
239 debugd::kGetInterfaces
);
240 debugdaemon_proxy_
->CallMethod(
242 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
243 base::Bind(&DebugDaemonClientImpl::OnGetNetworkInterfaces
,
244 weak_ptr_factory_
.GetWeakPtr(),
248 virtual void GetAllLogs(const GetLogsCallback
& callback
)
250 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
251 debugd::kGetAllLogs
);
252 debugdaemon_proxy_
->CallMethod(
254 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
255 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs
,
256 weak_ptr_factory_
.GetWeakPtr(),
260 virtual void GetUserLogFiles(
261 const GetLogsCallback
& callback
) OVERRIDE
{
262 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
263 debugd::kGetUserLogFiles
);
264 debugdaemon_proxy_
->CallMethod(
266 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
267 base::Bind(&DebugDaemonClientImpl::OnGetUserLogFiles
,
268 weak_ptr_factory_
.GetWeakPtr(),
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(
282 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
283 base::Bind(&DebugDaemonClientImpl::OnStartSystemTracing
,
284 weak_ptr_factory_
.GetWeakPtr()));
287 virtual bool RequestStopSystemTracing(const StopSystemTracingCallback
&
289 if (pipe_reader_
!= NULL
) {
290 LOG(ERROR
) << "Busy doing StopSystemTracing";
294 pipe_reader_
.reset(new PipeReader(
295 base::Bind(&DebugDaemonClientImpl::OnIOComplete
,
296 weak_ptr_factory_
.GetWeakPtr())));
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
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(
312 base::Bind(&DebugDaemonClientImpl::CheckValidity
,
314 base::Bind(&DebugDaemonClientImpl::OnCheckValidityRequestStopSystem
,
315 weak_ptr_factory_
.GetWeakPtr(),
316 base::Owned(file_descriptor
),
323 virtual void TestICMP(const std::string
& ip_address
,
324 const TestICMPCallback
& callback
) OVERRIDE
{
325 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
327 dbus::MessageWriter
writer(&method_call
);
328 writer
.AppendString(ip_address
);
329 debugdaemon_proxy_
->CallMethod(
331 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
332 base::Bind(&DebugDaemonClientImpl::OnTestICMP
,
333 weak_ptr_factory_
.GetWeakPtr(),
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(
355 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
356 base::Bind(&DebugDaemonClientImpl::OnGetDebugLogs
,
357 weak_ptr_factory_
.GetWeakPtr(),
361 // Called when a response for GetDebugLogs() is received.
362 void OnGetDebugLogs(const GetDebugLogsCallback
& callback
,
363 dbus::Response
* response
) {
365 LOG(ERROR
) << "Failed to get debug logs";
372 // Called when a response for SetDebugMode() is received.
373 void OnSetDebugMode(const SetDebugModeCallback
& callback
,
374 dbus::Response
* response
) {
376 LOG(ERROR
) << "Failed to change debug mode";
383 void OnGetRoutes(const GetRoutesCallback
& callback
,
384 dbus::Response
* response
) {
385 std::vector
<std::string
> routes
;
387 dbus::MessageReader
reader(response
);
388 if (reader
.PopArrayOfStrings(&routes
)) {
389 callback
.Run(true, routes
);
391 LOG(ERROR
) << "Got non-array response from GetRoutes";
392 callback
.Run(false, routes
);
395 callback
.Run(false, routes
);
399 void OnGetNetworkStatus(const GetNetworkStatusCallback
& callback
,
400 dbus::Response
* response
) {
402 if (response
&& dbus::MessageReader(response
).PopString(&status
))
403 callback
.Run(true, status
);
405 callback
.Run(false, "");
408 void OnGetModemStatus(const GetModemStatusCallback
& callback
,
409 dbus::Response
* response
) {
411 if (response
&& dbus::MessageReader(response
).PopString(&status
))
412 callback
.Run(true, status
);
414 callback
.Run(false, "");
417 void OnGetNetworkInterfaces(const GetNetworkInterfacesCallback
& callback
,
418 dbus::Response
* response
) {
420 if (response
&& dbus::MessageReader(response
).PopString(&status
))
421 callback
.Run(true, status
);
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
);
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
)) {
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
) {
457 LOG(ERROR
) << "Failed to request systrace start";
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(
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
) {
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
) {
499 if (response
&& dbus::MessageReader(response
).PopString(&status
))
500 callback
.Run(true, status
);
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
{
527 virtual void SetDebugMode(const std::string
& subsystem
,
528 const SetDebugModeCallback
& callback
) OVERRIDE
{
531 virtual void StartSystemTracing() OVERRIDE
{}
532 virtual bool RequestStopSystemTracing(const StopSystemTracingCallback
&
535 callback
.Run(base::RefCountedString::TakeString(&no_data
));
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
)
546 MessageLoop::current()->PostTask(FROM_HERE
,
547 base::Bind(callback
, false, ""));
549 virtual void GetModemStatus(const GetModemStatusCallback
& callback
)
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() {
584 DebugDaemonClient::StopSystemTracingCallback
585 DebugDaemonClient::EmptyStopSystemTracingCallback() {
586 return base::Bind(&EmptyStopSystemTracingCallbackBody
);
590 DebugDaemonClient
* DebugDaemonClient::Create(DBusClientImplementationType type
,
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