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.
4
5#include "chromeos/dbus/debug_daemon_client.h"
6
7#include <fcntl.h>
8#include <unistd.h>
9#include <string>
10#include <vector>
11
12#include "base/bind.h"
13#include "base/bind_helpers.h"
14#include "base/callback.h"
15#include "base/files/file_path.h"
16#include "base/location.h"
17#include "base/memory/ref_counted_memory.h"
18#include "base/message_loop/message_loop.h"
19#include "base/posix/eintr_wrapper.h"
20#include "base/strings/string_util.h"
21#include "base/task_runner_util.h"
22#include "chromeos/dbus/pipe_reader.h"
23#include "dbus/bus.h"
24#include "dbus/message.h"
25#include "dbus/object_path.h"
26#include "dbus/object_proxy.h"
27#include "third_party/cros_system_api/dbus/service_constants.h"
28
29namespace {
30
31// Used in DebugDaemonClient::EmptySystemStopTracingCallback().
32void EmptyStopSystemTracingCallbackBody(
33  const scoped_refptr<base::RefCountedString>& unused_result) {
34}
35
36}  // namespace
37
38namespace chromeos {
39
40// The DebugDaemonClient implementation used in production.
41class DebugDaemonClientImpl : public DebugDaemonClient {
42 public:
43  DebugDaemonClientImpl() : debugdaemon_proxy_(NULL), weak_ptr_factory_(this) {}
44
45  virtual ~DebugDaemonClientImpl() {}
46
47  // DebugDaemonClient override.
48  virtual void DumpDebugLogs(bool is_compressed,
49                             base::File file,
50                             scoped_refptr<base::TaskRunner> task_runner,
51                             const GetDebugLogsCallback& callback) OVERRIDE {
52    dbus::FileDescriptor* file_descriptor = new dbus::FileDescriptor;
53    file_descriptor->PutValue(file.TakePlatformFile());
54    // Punt descriptor validity check to a worker thread; on return we'll
55    // issue the D-Bus request to stop tracing and collect results.
56    task_runner->PostTaskAndReply(
57        FROM_HERE,
58        base::Bind(&dbus::FileDescriptor::CheckValidity,
59                   base::Unretained(file_descriptor)),
60        base::Bind(&DebugDaemonClientImpl::OnCheckValidityGetDebugLogs,
61                   weak_ptr_factory_.GetWeakPtr(),
62                   is_compressed,
63                   base::Owned(file_descriptor),
64                   callback));
65  }
66
67  virtual void SetDebugMode(const std::string& subsystem,
68                            const SetDebugModeCallback& callback) OVERRIDE {
69    dbus::MethodCall method_call(debugd::kDebugdInterface,
70                                 debugd::kSetDebugMode);
71    dbus::MessageWriter writer(&method_call);
72    writer.AppendString(subsystem);
73    debugdaemon_proxy_->CallMethod(
74        &method_call,
75        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
76        base::Bind(&DebugDaemonClientImpl::OnSetDebugMode,
77                   weak_ptr_factory_.GetWeakPtr(),
78                   callback));
79  }
80
81  virtual void GetRoutes(bool numeric, bool ipv6,
82                         const GetRoutesCallback& callback) OVERRIDE {
83    dbus::MethodCall method_call(debugd::kDebugdInterface,
84                                 debugd::kGetRoutes);
85    dbus::MessageWriter writer(&method_call);
86    dbus::MessageWriter sub_writer(NULL);
87    writer.OpenArray("{sv}", &sub_writer);
88    dbus::MessageWriter elem_writer(NULL);
89    sub_writer.OpenDictEntry(&elem_writer);
90    elem_writer.AppendString("numeric");
91    elem_writer.AppendVariantOfBool(numeric);
92    sub_writer.CloseContainer(&elem_writer);
93    sub_writer.OpenDictEntry(&elem_writer);
94    elem_writer.AppendString("v6");
95    elem_writer.AppendVariantOfBool(ipv6);
96    sub_writer.CloseContainer(&elem_writer);
97    writer.CloseContainer(&sub_writer);
98    debugdaemon_proxy_->CallMethod(
99        &method_call,
100        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
101        base::Bind(&DebugDaemonClientImpl::OnGetRoutes,
102                   weak_ptr_factory_.GetWeakPtr(),
103                   callback));
104  }
105
106  virtual void GetNetworkStatus(const GetNetworkStatusCallback& callback)
107      OVERRIDE {
108    dbus::MethodCall method_call(debugd::kDebugdInterface,
109                                 debugd::kGetNetworkStatus);
110    debugdaemon_proxy_->CallMethod(
111        &method_call,
112        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
113        base::Bind(&DebugDaemonClientImpl::OnGetNetworkStatus,
114                   weak_ptr_factory_.GetWeakPtr(),
115                   callback));
116  }
117
118  virtual void GetModemStatus(const GetModemStatusCallback& callback)
119      OVERRIDE {
120    dbus::MethodCall method_call(debugd::kDebugdInterface,
121                                 debugd::kGetModemStatus);
122    debugdaemon_proxy_->CallMethod(
123        &method_call,
124        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
125        base::Bind(&DebugDaemonClientImpl::OnGetModemStatus,
126                   weak_ptr_factory_.GetWeakPtr(),
127                   callback));
128  }
129
130  virtual void GetWiMaxStatus(const GetWiMaxStatusCallback& callback)
131      OVERRIDE {
132    dbus::MethodCall method_call(debugd::kDebugdInterface,
133                                 debugd::kGetWiMaxStatus);
134    debugdaemon_proxy_->CallMethod(
135        &method_call,
136        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
137        base::Bind(&DebugDaemonClientImpl::OnGetWiMaxStatus,
138                   weak_ptr_factory_.GetWeakPtr(),
139                   callback));
140  }
141
142  virtual void GetNetworkInterfaces(
143      const GetNetworkInterfacesCallback& callback) OVERRIDE {
144    dbus::MethodCall method_call(debugd::kDebugdInterface,
145                                 debugd::kGetInterfaces);
146    debugdaemon_proxy_->CallMethod(
147        &method_call,
148        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
149        base::Bind(&DebugDaemonClientImpl::OnGetNetworkInterfaces,
150                   weak_ptr_factory_.GetWeakPtr(),
151                   callback));
152  }
153
154  virtual void GetPerfData(uint32_t duration,
155                           const GetPerfDataCallback& callback) OVERRIDE {
156    dbus::MethodCall method_call(debugd::kDebugdInterface,
157                                 debugd::kGetRichPerfData);
158    dbus::MessageWriter writer(&method_call);
159    writer.AppendUint32(duration);
160
161    debugdaemon_proxy_->CallMethod(
162        &method_call,
163        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
164        base::Bind(&DebugDaemonClientImpl::OnGetPerfData,
165                   weak_ptr_factory_.GetWeakPtr(),
166                   callback));
167  }
168
169  virtual void GetScrubbedLogs(const GetLogsCallback& callback) OVERRIDE {
170    dbus::MethodCall method_call(debugd::kDebugdInterface,
171                                 debugd::kGetFeedbackLogs);
172    debugdaemon_proxy_->CallMethod(
173        &method_call,
174        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
175        base::Bind(&DebugDaemonClientImpl::OnGetAllLogs,
176                   weak_ptr_factory_.GetWeakPtr(),
177                   callback));
178  }
179
180  virtual void GetAllLogs(const GetLogsCallback& callback)
181      OVERRIDE {
182    dbus::MethodCall method_call(debugd::kDebugdInterface,
183                                 debugd::kGetAllLogs);
184    debugdaemon_proxy_->CallMethod(
185        &method_call,
186        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
187        base::Bind(&DebugDaemonClientImpl::OnGetAllLogs,
188                   weak_ptr_factory_.GetWeakPtr(),
189                   callback));
190  }
191
192  virtual void GetUserLogFiles(
193      const GetLogsCallback& callback) OVERRIDE {
194    dbus::MethodCall method_call(debugd::kDebugdInterface,
195                                 debugd::kGetUserLogFiles);
196    debugdaemon_proxy_->CallMethod(
197        &method_call,
198        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
199        base::Bind(&DebugDaemonClientImpl::OnGetUserLogFiles,
200                   weak_ptr_factory_.GetWeakPtr(),
201                   callback));
202  }
203
204  virtual void StartSystemTracing() OVERRIDE {
205    dbus::MethodCall method_call(
206        debugd::kDebugdInterface,
207        debugd::kSystraceStart);
208    dbus::MessageWriter writer(&method_call);
209    writer.AppendString("all"); // TODO(sleffler) parameterize category list
210
211    DVLOG(1) << "Requesting a systrace start";
212    debugdaemon_proxy_->CallMethod(
213        &method_call,
214        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
215        base::Bind(&DebugDaemonClientImpl::OnStartMethod,
216                   weak_ptr_factory_.GetWeakPtr()));
217  }
218
219  virtual bool RequestStopSystemTracing(
220      scoped_refptr<base::TaskRunner> task_runner,
221      const StopSystemTracingCallback& callback) OVERRIDE {
222    if (pipe_reader_ != NULL) {
223      LOG(ERROR) << "Busy doing StopSystemTracing";
224      return false;
225    }
226
227    pipe_reader_.reset(new PipeReaderForString(
228        task_runner,
229        base::Bind(&DebugDaemonClientImpl::OnIOComplete,
230                   weak_ptr_factory_.GetWeakPtr())));
231
232    base::File pipe_write_end = pipe_reader_->StartIO();
233    // Create dbus::FileDescriptor on the worker thread; on return we'll
234    // issue the D-Bus request to stop tracing and collect results.
235    base::PostTaskAndReplyWithResult(
236        task_runner.get(),
237        FROM_HERE,
238        base::Bind(
239            &DebugDaemonClientImpl::CreateFileDescriptorToStopSystemTracing,
240            base::Passed(&pipe_write_end)),
241        base::Bind(
242            &DebugDaemonClientImpl::OnCreateFileDescriptorRequestStopSystem,
243            weak_ptr_factory_.GetWeakPtr(),
244            callback));
245    return true;
246  }
247
248  virtual void TestICMP(const std::string& ip_address,
249                        const TestICMPCallback& callback) OVERRIDE {
250    dbus::MethodCall method_call(debugd::kDebugdInterface,
251                                 debugd::kTestICMP);
252    dbus::MessageWriter writer(&method_call);
253    writer.AppendString(ip_address);
254    debugdaemon_proxy_->CallMethod(
255        &method_call,
256        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
257        base::Bind(&DebugDaemonClientImpl::OnTestICMP,
258                   weak_ptr_factory_.GetWeakPtr(),
259                   callback));
260  }
261
262  virtual void TestICMPWithOptions(
263      const std::string& ip_address,
264      const std::map<std::string, std::string>& options,
265      const TestICMPCallback& callback) OVERRIDE {
266    dbus::MethodCall method_call(debugd::kDebugdInterface,
267                                 debugd::kTestICMPWithOptions);
268    dbus::MessageWriter writer(&method_call);
269    dbus::MessageWriter sub_writer(NULL);
270    dbus::MessageWriter elem_writer(NULL);
271
272    // Write the host.
273    writer.AppendString(ip_address);
274
275    // Write the options.
276    writer.OpenArray("{ss}", &sub_writer);
277    std::map<std::string, std::string>::const_iterator it;
278    for (it = options.begin(); it != options.end(); ++it) {
279      sub_writer.OpenDictEntry(&elem_writer);
280      elem_writer.AppendString(it->first);
281      elem_writer.AppendString(it->second);
282      sub_writer.CloseContainer(&elem_writer);
283    }
284    writer.CloseContainer(&sub_writer);
285
286    // Call the function.
287    debugdaemon_proxy_->CallMethod(
288        &method_call,
289        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
290        base::Bind(&DebugDaemonClientImpl::OnTestICMP,
291                   weak_ptr_factory_.GetWeakPtr(),
292                   callback));
293  }
294
295  virtual void UploadCrashes() OVERRIDE {
296    dbus::MethodCall method_call(debugd::kDebugdInterface,
297                                 debugd::kUploadCrashes);
298    debugdaemon_proxy_->CallMethod(
299        &method_call,
300        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
301        base::Bind(&DebugDaemonClientImpl::OnStartMethod,
302                   weak_ptr_factory_.GetWeakPtr()));
303  }
304
305 protected:
306  virtual void Init(dbus::Bus* bus) OVERRIDE {
307    debugdaemon_proxy_ =
308        bus->GetObjectProxy(debugd::kDebugdServiceName,
309                            dbus::ObjectPath(debugd::kDebugdServicePath));
310  }
311
312 private:
313  // Called when a CheckValidity response is received.
314  void OnCheckValidityGetDebugLogs(bool is_compressed,
315                                   dbus::FileDescriptor* file_descriptor,
316                                   const GetDebugLogsCallback& callback) {
317    // Issue the dbus request to get debug logs.
318    dbus::MethodCall method_call(debugd::kDebugdInterface,
319                                 debugd::kDumpDebugLogs);
320    dbus::MessageWriter writer(&method_call);
321    writer.AppendBool(is_compressed);
322    writer.AppendFileDescriptor(*file_descriptor);
323
324    debugdaemon_proxy_->CallMethod(
325        &method_call,
326        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
327        base::Bind(&DebugDaemonClientImpl::OnGetDebugLogs,
328                   weak_ptr_factory_.GetWeakPtr(),
329                   callback));
330  }
331
332  // Called when a response for GetDebugLogs() is received.
333  void OnGetDebugLogs(const GetDebugLogsCallback& callback,
334                      dbus::Response* response) {
335    if (!response) {
336      LOG(ERROR) << "Failed to get debug logs";
337      callback.Run(false);
338      return;
339    }
340    callback.Run(true);
341  }
342
343  // Called when a response for SetDebugMode() is received.
344  void OnSetDebugMode(const SetDebugModeCallback& callback,
345                      dbus::Response* response) {
346    if (!response) {
347      LOG(ERROR) << "Failed to change debug mode";
348      callback.Run(false);
349    } else {
350      callback.Run(true);
351    }
352  }
353
354  void OnGetRoutes(const GetRoutesCallback& callback,
355                   dbus::Response* response) {
356    std::vector<std::string> routes;
357    if (response) {
358      dbus::MessageReader reader(response);
359      if (reader.PopArrayOfStrings(&routes)) {
360        callback.Run(true, routes);
361      } else {
362        LOG(ERROR) << "Got non-array response from GetRoutes";
363        callback.Run(false, routes);
364      }
365    } else {
366      callback.Run(false, routes);
367    }
368  }
369
370  void OnGetNetworkStatus(const GetNetworkStatusCallback& callback,
371                          dbus::Response* response) {
372    std::string status;
373    if (response && dbus::MessageReader(response).PopString(&status))
374      callback.Run(true, status);
375    else
376      callback.Run(false, "");
377  }
378
379  void OnGetModemStatus(const GetModemStatusCallback& callback,
380                        dbus::Response* response) {
381    std::string status;
382    if (response && dbus::MessageReader(response).PopString(&status))
383      callback.Run(true, status);
384    else
385      callback.Run(false, "");
386  }
387
388  void OnGetWiMaxStatus(const GetWiMaxStatusCallback& callback,
389                        dbus::Response* response) {
390    std::string status;
391    if (response && dbus::MessageReader(response).PopString(&status))
392      callback.Run(true, status);
393    else
394      callback.Run(false, "");
395  }
396
397  void OnGetNetworkInterfaces(const GetNetworkInterfacesCallback& callback,
398                              dbus::Response* response) {
399    std::string status;
400    if (response && dbus::MessageReader(response).PopString(&status))
401      callback.Run(true, status);
402    else
403      callback.Run(false, "");
404  }
405
406  void OnGetPerfData(const GetPerfDataCallback& callback,
407                     dbus::Response* response) {
408    std::vector<uint8> data;
409
410    if (!response) {
411      return;
412    }
413
414    dbus::MessageReader reader(response);
415    const uint8* buffer = NULL;
416    size_t buf_size = 0;
417    if (!reader.PopArrayOfBytes(&buffer, &buf_size))
418      return;
419
420    // TODO(asharif): Figure out a way to avoid this copy.
421    data.insert(data.end(), buffer, buffer + buf_size);
422
423    callback.Run(data);
424  }
425
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;
434    }
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;
443      }
444      logs[key] = value;
445    }
446    callback.Run(!sub_reader.HasMoreData() && !broken, logs);
447  }
448
449  void OnGetUserLogFiles(const GetLogsCallback& callback,
450                         dbus::Response* response) {
451    return OnGetAllLogs(callback, response);
452  }
453
454  // Called when a response for a simple start is received.
455  void OnStartMethod(dbus::Response* response) {
456    if (!response) {
457      LOG(ERROR) << "Failed to request start";
458      return;
459    }
460  }
461
462  // Creates dbus::FileDescriptor from base::File.
463  static scoped_ptr<dbus::FileDescriptor>
464  CreateFileDescriptorToStopSystemTracing(base::File pipe_write_end) {
465    if (!pipe_write_end.IsValid()) {
466      LOG(ERROR) << "Cannot create pipe reader";
467      // NB: continue anyway to shutdown tracing; toss trace data
468      pipe_write_end.Initialize(base::FilePath(FILE_PATH_LITERAL("/dev/null")),
469                                base::File::FLAG_OPEN | base::File::FLAG_WRITE);
470      // TODO(sleffler) if this fails AppendFileDescriptor will abort
471    }
472    scoped_ptr<dbus::FileDescriptor> file_descriptor(new dbus::FileDescriptor);
473    file_descriptor->PutValue(pipe_write_end.TakePlatformFile());
474    file_descriptor->CheckValidity();
475    return file_descriptor.Pass();
476  }
477
478  // Called when a CheckValidity response is received.
479  void OnCreateFileDescriptorRequestStopSystem(
480      const StopSystemTracingCallback& callback,
481      scoped_ptr<dbus::FileDescriptor> file_descriptor) {
482    DCHECK(file_descriptor);
483
484    // Issue the dbus request to stop system tracing
485    dbus::MethodCall method_call(
486        debugd::kDebugdInterface,
487        debugd::kSystraceStop);
488    dbus::MessageWriter writer(&method_call);
489    writer.AppendFileDescriptor(*file_descriptor);
490
491    callback_ = callback;
492
493    DVLOG(1) << "Requesting a systrace stop";
494    debugdaemon_proxy_->CallMethod(
495        &method_call,
496        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
497        base::Bind(&DebugDaemonClientImpl::OnRequestStopSystemTracing,
498                   weak_ptr_factory_.GetWeakPtr()));
499  }
500
501  // Called when a response for RequestStopSystemTracing() is received.
502  void OnRequestStopSystemTracing(dbus::Response* response) {
503    if (!response) {
504      LOG(ERROR) << "Failed to request systrace stop";
505      // If debugd crashes or completes I/O before this message is processed
506      // then pipe_reader_ can be NULL, see OnIOComplete().
507      if (pipe_reader_.get())
508        pipe_reader_->OnDataReady(-1); // terminate data stream
509    }
510    // NB: requester is signaled when i/o completes
511  }
512
513  void OnTestICMP(const TestICMPCallback& callback, dbus::Response* response) {
514    std::string status;
515    if (response && dbus::MessageReader(response).PopString(&status))
516      callback.Run(true, status);
517    else
518      callback.Run(false, "");
519  }
520
521  // Called when pipe i/o completes; pass data on and delete the instance.
522  void OnIOComplete() {
523    std::string pipe_data;
524    pipe_reader_->GetData(&pipe_data);
525    callback_.Run(base::RefCountedString::TakeString(&pipe_data));
526    pipe_reader_.reset();
527  }
528
529  dbus::ObjectProxy* debugdaemon_proxy_;
530  scoped_ptr<PipeReaderForString> pipe_reader_;
531  StopSystemTracingCallback callback_;
532  base::WeakPtrFactory<DebugDaemonClientImpl> weak_ptr_factory_;
533
534  DISALLOW_COPY_AND_ASSIGN(DebugDaemonClientImpl);
535};
536
537DebugDaemonClient::DebugDaemonClient() {
538}
539
540DebugDaemonClient::~DebugDaemonClient() {
541}
542
543// static
544DebugDaemonClient::StopSystemTracingCallback
545DebugDaemonClient::EmptyStopSystemTracingCallback() {
546  return base::Bind(&EmptyStopSystemTracingCallbackBody);
547}
548
549// static
550DebugDaemonClient* DebugDaemonClient::Create() {
551  return new DebugDaemonClientImpl();
552}
553
554}  // namespace chromeos
555