chromeos: Make dbus::MessageReader memory ownership explicit
[chromium-blink-merge.git] / chromeos / dbus / debug_daemon_client.cc
blob25a55e331f75946d92088a2da819e140743e3329
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 "chromeos/dbus/debug_daemon_client.h"
7 #include <fcntl.h>
8 #include <unistd.h>
9 #include <string>
10 #include <vector>
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/callback.h"
15 #include "base/location.h"
16 #include "base/memory/ref_counted_memory.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/platform_file.h"
19 #include "base/posix/eintr_wrapper.h"
20 #include "base/strings/string_util.h"
21 #include "base/threading/worker_pool.h"
22 #include "dbus/bus.h"
23 #include "dbus/message.h"
24 #include "dbus/object_path.h"
25 #include "dbus/object_proxy.h"
26 #include "net/base/file_stream.h"
27 #include "net/base/io_buffer.h"
28 #include "net/base/net_errors.h"
29 #include "third_party/cros_system_api/dbus/service_constants.h"
31 namespace {
33 // Used in DebugDaemonClient::EmptySystemStopTracingCallback().
34 void EmptyStopSystemTracingCallbackBody(
35 const scoped_refptr<base::RefCountedString>& unused_result) {
38 // Simple class to encapsulate collecting data from a pipe into a
39 // string. To use, instantiate the class, start i/o, and then delete
40 // the instance on callback. The data should be retrieved before
41 // delete and extracted or copied.
43 // TODO(sleffler) move data collection to a sub-class so this
44 // can be reused to process data as it is received
45 class PipeReader {
46 public:
47 typedef base::Callback<void(void)>IOCompleteCallback;
49 explicit PipeReader(IOCompleteCallback callback)
50 : io_buffer_(new net::IOBufferWithSize(4096)),
51 callback_(callback),
52 weak_ptr_factory_(this) {
53 pipe_fd_[0] = pipe_fd_[1] = -1;
56 virtual ~PipeReader() {
57 // Don't close pipe_fd_[0] as it's closed by data_stream_.
58 if (pipe_fd_[1] != -1)
59 if (IGNORE_EINTR(close(pipe_fd_[1])) < 0)
60 PLOG(ERROR) << "close[1]";
63 // Returns descriptor for the writeable side of the pipe.
64 int GetWriteFD() { return pipe_fd_[1]; }
66 // Closes writeable descriptor; normally used in parent process after fork.
67 void CloseWriteFD() {
68 if (pipe_fd_[1] != -1) {
69 if (IGNORE_EINTR(close(pipe_fd_[1])) < 0)
70 PLOG(ERROR) << "close";
71 pipe_fd_[1] = -1;
75 // Returns collected data.
76 std::string* data() { return &data_; }
78 // Starts data collection. Returns true if stream was setup correctly.
79 // On success data will automatically be accumulated into a string that
80 // can be retrieved with PipeReader::data(). To shutdown collection delete
81 // the instance and/or use PipeReader::OnDataReady(-1).
82 bool StartIO() {
83 // Use a pipe to collect data
84 const int status = HANDLE_EINTR(pipe(pipe_fd_));
85 if (status < 0) {
86 PLOG(ERROR) << "pipe";
87 return false;
89 base::PlatformFile data_file_ = pipe_fd_[0]; // read side
90 data_stream_.reset(new net::FileStream(data_file_,
91 base::PLATFORM_FILE_READ | base::PLATFORM_FILE_ASYNC,
92 NULL));
94 // Post an initial async read to setup data collection
95 int rv = data_stream_->Read(io_buffer_.get(), io_buffer_->size(),
96 base::Bind(&PipeReader::OnDataReady, weak_ptr_factory_.GetWeakPtr()));
97 if (rv != net::ERR_IO_PENDING) {
98 LOG(ERROR) << "Unable to post initial read";
99 return false;
101 return true;
104 // Called when pipe data are available. Can also be used to shutdown
105 // data collection by passing -1 for |byte_count|.
106 void OnDataReady(int byte_count) {
107 DVLOG(1) << "OnDataReady byte_count " << byte_count;
108 if (byte_count <= 0) {
109 callback_.Run(); // signal creator to take data and delete us
110 return;
112 data_.append(io_buffer_->data(), byte_count);
114 // Post another read
115 int rv = data_stream_->Read(io_buffer_.get(), io_buffer_->size(),
116 base::Bind(&PipeReader::OnDataReady, weak_ptr_factory_.GetWeakPtr()));
117 if (rv != net::ERR_IO_PENDING) {
118 LOG(ERROR) << "Unable to post another read";
119 // TODO(sleffler) do something more intelligent?
123 private:
124 friend class base::RefCounted<PipeReader>;
126 int pipe_fd_[2];
127 scoped_ptr<net::FileStream> data_stream_;
128 scoped_refptr<net::IOBufferWithSize> io_buffer_;
129 std::string data_;
130 IOCompleteCallback callback_;
132 // Note: This should remain the last member so it'll be destroyed and
133 // invalidate its weak pointers before any other members are destroyed.
134 base::WeakPtrFactory<PipeReader> weak_ptr_factory_;
136 DISALLOW_COPY_AND_ASSIGN(PipeReader);
139 } // namespace
141 namespace chromeos {
143 // The DebugDaemonClient implementation used in production.
144 class DebugDaemonClientImpl : public DebugDaemonClient {
145 public:
146 DebugDaemonClientImpl() : debugdaemon_proxy_(NULL), weak_ptr_factory_(this) {}
148 virtual ~DebugDaemonClientImpl() {}
150 // DebugDaemonClient override.
151 virtual void GetDebugLogs(base::PlatformFile file,
152 const GetDebugLogsCallback& callback) OVERRIDE {
154 dbus::FileDescriptor* file_descriptor = new dbus::FileDescriptor(file);
155 // Punt descriptor validity check to a worker thread; on return we'll
156 // issue the D-Bus request to stop tracing and collect results.
157 base::WorkerPool::PostTaskAndReply(
158 FROM_HERE,
159 base::Bind(&DebugDaemonClientImpl::CheckValidity,
160 file_descriptor),
161 base::Bind(&DebugDaemonClientImpl::OnCheckValidityGetDebugLogs,
162 weak_ptr_factory_.GetWeakPtr(),
163 base::Owned(file_descriptor),
164 callback),
165 false);
168 virtual void SetDebugMode(const std::string& subsystem,
169 const SetDebugModeCallback& callback) OVERRIDE {
170 dbus::MethodCall method_call(debugd::kDebugdInterface,
171 debugd::kSetDebugMode);
172 dbus::MessageWriter writer(&method_call);
173 writer.AppendString(subsystem);
174 debugdaemon_proxy_->CallMethod(
175 &method_call,
176 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
177 base::Bind(&DebugDaemonClientImpl::OnSetDebugMode,
178 weak_ptr_factory_.GetWeakPtr(),
179 callback));
182 virtual void GetRoutes(bool numeric, bool ipv6,
183 const GetRoutesCallback& callback) OVERRIDE {
184 dbus::MethodCall method_call(debugd::kDebugdInterface,
185 debugd::kGetRoutes);
186 dbus::MessageWriter writer(&method_call);
187 dbus::MessageWriter sub_writer(NULL);
188 writer.OpenArray("{sv}", &sub_writer);
189 dbus::MessageWriter elem_writer(NULL);
190 sub_writer.OpenDictEntry(&elem_writer);
191 elem_writer.AppendString("numeric");
192 elem_writer.AppendVariantOfBool(numeric);
193 sub_writer.CloseContainer(&elem_writer);
194 sub_writer.OpenDictEntry(&elem_writer);
195 elem_writer.AppendString("v6");
196 elem_writer.AppendVariantOfBool(ipv6);
197 sub_writer.CloseContainer(&elem_writer);
198 writer.CloseContainer(&sub_writer);
199 debugdaemon_proxy_->CallMethod(
200 &method_call,
201 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
202 base::Bind(&DebugDaemonClientImpl::OnGetRoutes,
203 weak_ptr_factory_.GetWeakPtr(),
204 callback));
207 virtual void GetNetworkStatus(const GetNetworkStatusCallback& callback)
208 OVERRIDE {
209 dbus::MethodCall method_call(debugd::kDebugdInterface,
210 debugd::kGetNetworkStatus);
211 debugdaemon_proxy_->CallMethod(
212 &method_call,
213 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
214 base::Bind(&DebugDaemonClientImpl::OnGetNetworkStatus,
215 weak_ptr_factory_.GetWeakPtr(),
216 callback));
219 virtual void GetModemStatus(const GetModemStatusCallback& callback)
220 OVERRIDE {
221 dbus::MethodCall method_call(debugd::kDebugdInterface,
222 debugd::kGetModemStatus);
223 debugdaemon_proxy_->CallMethod(
224 &method_call,
225 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
226 base::Bind(&DebugDaemonClientImpl::OnGetModemStatus,
227 weak_ptr_factory_.GetWeakPtr(),
228 callback));
231 virtual void GetWiMaxStatus(const GetWiMaxStatusCallback& callback)
232 OVERRIDE {
233 dbus::MethodCall method_call(debugd::kDebugdInterface,
234 debugd::kGetWiMaxStatus);
235 debugdaemon_proxy_->CallMethod(
236 &method_call,
237 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
238 base::Bind(&DebugDaemonClientImpl::OnGetWiMaxStatus,
239 weak_ptr_factory_.GetWeakPtr(),
240 callback));
243 virtual void GetNetworkInterfaces(
244 const GetNetworkInterfacesCallback& callback) OVERRIDE {
245 dbus::MethodCall method_call(debugd::kDebugdInterface,
246 debugd::kGetInterfaces);
247 debugdaemon_proxy_->CallMethod(
248 &method_call,
249 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
250 base::Bind(&DebugDaemonClientImpl::OnGetNetworkInterfaces,
251 weak_ptr_factory_.GetWeakPtr(),
252 callback));
255 virtual void GetPerfData(uint32_t duration,
256 const GetPerfDataCallback& callback) OVERRIDE {
257 dbus::MethodCall method_call(debugd::kDebugdInterface,
258 debugd::kGetRichPerfData);
259 dbus::MessageWriter writer(&method_call);
260 writer.AppendUint32(duration);
262 debugdaemon_proxy_->CallMethod(
263 &method_call,
264 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
265 base::Bind(&DebugDaemonClientImpl::OnGetPerfData,
266 weak_ptr_factory_.GetWeakPtr(),
267 callback));
270 virtual void GetScrubbedLogs(const GetLogsCallback& callback) OVERRIDE {
271 dbus::MethodCall method_call(debugd::kDebugdInterface,
272 debugd::kGetFeedbackLogs);
273 debugdaemon_proxy_->CallMethod(
274 &method_call,
275 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
276 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs,
277 weak_ptr_factory_.GetWeakPtr(),
278 callback));
281 virtual void GetAllLogs(const GetLogsCallback& callback)
282 OVERRIDE {
283 dbus::MethodCall method_call(debugd::kDebugdInterface,
284 debugd::kGetAllLogs);
285 debugdaemon_proxy_->CallMethod(
286 &method_call,
287 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
288 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs,
289 weak_ptr_factory_.GetWeakPtr(),
290 callback));
293 virtual void GetUserLogFiles(
294 const GetLogsCallback& callback) OVERRIDE {
295 dbus::MethodCall method_call(debugd::kDebugdInterface,
296 debugd::kGetUserLogFiles);
297 debugdaemon_proxy_->CallMethod(
298 &method_call,
299 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
300 base::Bind(&DebugDaemonClientImpl::OnGetUserLogFiles,
301 weak_ptr_factory_.GetWeakPtr(),
302 callback));
305 virtual void StartSystemTracing() OVERRIDE {
306 dbus::MethodCall method_call(
307 debugd::kDebugdInterface,
308 debugd::kSystraceStart);
309 dbus::MessageWriter writer(&method_call);
310 writer.AppendString("all"); // TODO(sleffler) parameterize category list
312 DVLOG(1) << "Requesting a systrace start";
313 debugdaemon_proxy_->CallMethod(
314 &method_call,
315 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
316 base::Bind(&DebugDaemonClientImpl::OnStartSystemTracing,
317 weak_ptr_factory_.GetWeakPtr()));
320 virtual bool RequestStopSystemTracing(const StopSystemTracingCallback&
321 callback) OVERRIDE {
322 if (pipe_reader_ != NULL) {
323 LOG(ERROR) << "Busy doing StopSystemTracing";
324 return false;
327 pipe_reader_.reset(new PipeReader(
328 base::Bind(&DebugDaemonClientImpl::OnIOComplete,
329 weak_ptr_factory_.GetWeakPtr())));
330 int write_fd = -1;
331 if (!pipe_reader_->StartIO()) {
332 LOG(ERROR) << "Cannot create pipe reader";
333 // NB: continue anyway to shutdown tracing; toss trace data
334 write_fd = HANDLE_EINTR(open("/dev/null", O_WRONLY));
335 // TODO(sleffler) if this fails AppendFileDescriptor will abort
336 } else {
337 write_fd = pipe_reader_->GetWriteFD();
340 dbus::FileDescriptor* file_descriptor = new dbus::FileDescriptor(write_fd);
341 // Punt descriptor validity check to a worker thread; on return we'll
342 // issue the D-Bus request to stop tracing and collect results.
343 base::WorkerPool::PostTaskAndReply(
344 FROM_HERE,
345 base::Bind(&DebugDaemonClientImpl::CheckValidity,
346 file_descriptor),
347 base::Bind(&DebugDaemonClientImpl::OnCheckValidityRequestStopSystem,
348 weak_ptr_factory_.GetWeakPtr(),
349 base::Owned(file_descriptor),
350 callback),
351 false);
353 return true;
356 virtual void TestICMP(const std::string& ip_address,
357 const TestICMPCallback& callback) OVERRIDE {
358 dbus::MethodCall method_call(debugd::kDebugdInterface,
359 debugd::kTestICMP);
360 dbus::MessageWriter writer(&method_call);
361 writer.AppendString(ip_address);
362 debugdaemon_proxy_->CallMethod(
363 &method_call,
364 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
365 base::Bind(&DebugDaemonClientImpl::OnTestICMP,
366 weak_ptr_factory_.GetWeakPtr(),
367 callback));
370 virtual void TestICMPWithOptions(
371 const std::string& ip_address,
372 const std::map<std::string, std::string>& options,
373 const TestICMPCallback& callback) OVERRIDE {
374 dbus::MethodCall method_call(debugd::kDebugdInterface,
375 debugd::kTestICMPWithOptions);
376 dbus::MessageWriter writer(&method_call);
377 dbus::MessageWriter sub_writer(NULL);
378 dbus::MessageWriter elem_writer(NULL);
380 // Write the host.
381 writer.AppendString(ip_address);
383 // Write the options.
384 writer.OpenArray("{ss}", &sub_writer);
385 std::map<std::string, std::string>::const_iterator it;
386 for (it = options.begin(); it != options.end(); ++it) {
387 sub_writer.OpenDictEntry(&elem_writer);
388 elem_writer.AppendString(it->first);
389 elem_writer.AppendString(it->second);
390 sub_writer.CloseContainer(&elem_writer);
392 writer.CloseContainer(&sub_writer);
394 // Call the function.
395 debugdaemon_proxy_->CallMethod(
396 &method_call,
397 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
398 base::Bind(&DebugDaemonClientImpl::OnTestICMP,
399 weak_ptr_factory_.GetWeakPtr(),
400 callback));
403 protected:
404 virtual void Init(dbus::Bus* bus) OVERRIDE {
405 debugdaemon_proxy_ =
406 bus->GetObjectProxy(debugd::kDebugdServiceName,
407 dbus::ObjectPath(debugd::kDebugdServicePath));
410 private:
411 // Called to check descriptor validity on a thread where i/o is permitted.
412 static void CheckValidity(dbus::FileDescriptor* file_descriptor) {
413 file_descriptor->CheckValidity();
416 // Called when a CheckValidity response is received.
417 void OnCheckValidityGetDebugLogs(dbus::FileDescriptor* file_descriptor,
418 const GetDebugLogsCallback& callback) {
419 // Issue the dbus request to get debug logs.
420 dbus::MethodCall method_call(
421 debugd::kDebugdInterface,
422 debugd::kGetDebugLogs);
423 dbus::MessageWriter writer(&method_call);
424 writer.AppendFileDescriptor(*file_descriptor);
426 debugdaemon_proxy_->CallMethod(
427 &method_call,
428 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
429 base::Bind(&DebugDaemonClientImpl::OnGetDebugLogs,
430 weak_ptr_factory_.GetWeakPtr(),
431 callback));
434 // Called when a response for GetDebugLogs() is received.
435 void OnGetDebugLogs(const GetDebugLogsCallback& callback,
436 dbus::Response* response) {
437 if (!response) {
438 LOG(ERROR) << "Failed to get debug logs";
439 callback.Run(false);
440 return;
442 callback.Run(true);
445 // Called when a response for SetDebugMode() is received.
446 void OnSetDebugMode(const SetDebugModeCallback& callback,
447 dbus::Response* response) {
448 if (!response) {
449 LOG(ERROR) << "Failed to change debug mode";
450 callback.Run(false);
451 } else {
452 callback.Run(true);
456 void OnGetRoutes(const GetRoutesCallback& callback,
457 dbus::Response* response) {
458 std::vector<std::string> routes;
459 if (response) {
460 dbus::MessageReader reader(response);
461 if (reader.PopArrayOfStrings(&routes)) {
462 callback.Run(true, routes);
463 } else {
464 LOG(ERROR) << "Got non-array response from GetRoutes";
465 callback.Run(false, routes);
467 } else {
468 callback.Run(false, routes);
472 void OnGetNetworkStatus(const GetNetworkStatusCallback& callback,
473 dbus::Response* response) {
474 std::string status;
475 if (response && dbus::MessageReader(response).PopString(&status))
476 callback.Run(true, status);
477 else
478 callback.Run(false, "");
481 void OnGetModemStatus(const GetModemStatusCallback& callback,
482 dbus::Response* response) {
483 std::string status;
484 if (response && dbus::MessageReader(response).PopString(&status))
485 callback.Run(true, status);
486 else
487 callback.Run(false, "");
490 void OnGetWiMaxStatus(const GetWiMaxStatusCallback& callback,
491 dbus::Response* response) {
492 std::string status;
493 if (response && dbus::MessageReader(response).PopString(&status))
494 callback.Run(true, status);
495 else
496 callback.Run(false, "");
499 void OnGetNetworkInterfaces(const GetNetworkInterfacesCallback& callback,
500 dbus::Response* response) {
501 std::string status;
502 if (response && dbus::MessageReader(response).PopString(&status))
503 callback.Run(true, status);
504 else
505 callback.Run(false, "");
508 void OnGetPerfData(const GetPerfDataCallback& callback,
509 dbus::Response* response) {
510 std::vector<uint8> data;
512 if (!response) {
513 return;
516 dbus::MessageReader reader(response);
517 const uint8* buffer = NULL;
518 size_t buf_size = 0;
519 if (!reader.PopArrayOfBytes(&buffer, &buf_size))
520 return;
522 // TODO(asharif): Figure out a way to avoid this copy.
523 data.insert(data.end(), buffer, buffer + buf_size);
525 callback.Run(data);
528 void OnGetAllLogs(const GetLogsCallback& callback,
529 dbus::Response* response) {
530 std::map<std::string, std::string> logs;
531 bool broken = false; // did we see a broken (k,v) pair?
532 dbus::MessageReader sub_reader(NULL);
533 if (!response || !dbus::MessageReader(response).PopArray(&sub_reader)) {
534 callback.Run(false, logs);
535 return;
537 while (sub_reader.HasMoreData()) {
538 dbus::MessageReader sub_sub_reader(NULL);
539 std::string key, value;
540 if (!sub_reader.PopDictEntry(&sub_sub_reader)
541 || !sub_sub_reader.PopString(&key)
542 || !sub_sub_reader.PopString(&value)) {
543 broken = true;
544 break;
546 logs[key] = value;
548 callback.Run(!sub_reader.HasMoreData() && !broken, logs);
551 void OnGetUserLogFiles(const GetLogsCallback& callback,
552 dbus::Response* response) {
553 return OnGetAllLogs(callback, response);
556 // Called when a response for StartSystemTracing() is received.
557 void OnStartSystemTracing(dbus::Response* response) {
558 if (!response) {
559 LOG(ERROR) << "Failed to request systrace start";
560 return;
564 // Called when a CheckValidity response is received.
565 void OnCheckValidityRequestStopSystem(
566 dbus::FileDescriptor* file_descriptor,
567 const StopSystemTracingCallback& callback) {
568 // Issue the dbus request to stop system tracing
569 dbus::MethodCall method_call(
570 debugd::kDebugdInterface,
571 debugd::kSystraceStop);
572 dbus::MessageWriter writer(&method_call);
573 writer.AppendFileDescriptor(*file_descriptor);
575 callback_ = callback;
577 DVLOG(1) << "Requesting a systrace stop";
578 debugdaemon_proxy_->CallMethod(
579 &method_call,
580 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
581 base::Bind(&DebugDaemonClientImpl::OnRequestStopSystemTracing,
582 weak_ptr_factory_.GetWeakPtr()));
584 pipe_reader_->CloseWriteFD(); // close our copy of fd after send
587 // Called when a response for RequestStopSystemTracing() is received.
588 void OnRequestStopSystemTracing(dbus::Response* response) {
589 if (!response) {
590 LOG(ERROR) << "Failed to request systrace stop";
591 // If debugd crashes or completes I/O before this message is processed
592 // then pipe_reader_ can be NULL, see OnIOComplete().
593 if (pipe_reader_.get())
594 pipe_reader_->OnDataReady(-1); // terminate data stream
596 // NB: requester is signaled when i/o completes
599 void OnTestICMP(const TestICMPCallback& callback, dbus::Response* response) {
600 std::string status;
601 if (response && dbus::MessageReader(response).PopString(&status))
602 callback.Run(true, status);
603 else
604 callback.Run(false, "");
607 // Called when pipe i/o completes; pass data on and delete the instance.
608 void OnIOComplete() {
609 callback_.Run(base::RefCountedString::TakeString(pipe_reader_->data()));
610 pipe_reader_.reset();
613 dbus::ObjectProxy* debugdaemon_proxy_;
614 scoped_ptr<PipeReader> pipe_reader_;
615 StopSystemTracingCallback callback_;
616 base::WeakPtrFactory<DebugDaemonClientImpl> weak_ptr_factory_;
618 DISALLOW_COPY_AND_ASSIGN(DebugDaemonClientImpl);
621 DebugDaemonClient::DebugDaemonClient() {
624 DebugDaemonClient::~DebugDaemonClient() {
627 // static
628 DebugDaemonClient::StopSystemTracingCallback
629 DebugDaemonClient::EmptyStopSystemTracingCallback() {
630 return base::Bind(&EmptyStopSystemTracingCallbackBody);
633 // static
634 DebugDaemonClient* DebugDaemonClient::Create() {
635 return new DebugDaemonClientImpl();
638 } // namespace chromeos