Home | History | Annotate | Download | only in dbus
      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 
     29 namespace {
     30 
     31 // Used in DebugDaemonClient::EmptySystemStopTracingCallback().
     32 void EmptyStopSystemTracingCallbackBody(
     33   const scoped_refptr<base::RefCountedString>& unused_result) {
     34 }
     35 
     36 }  // namespace
     37 
     38 namespace chromeos {
     39 
     40 // The DebugDaemonClient implementation used in production.
     41 class 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 
    537 DebugDaemonClient::DebugDaemonClient() {
    538 }
    539 
    540 DebugDaemonClient::~DebugDaemonClient() {
    541 }
    542 
    543 // static
    544 DebugDaemonClient::StopSystemTracingCallback
    545 DebugDaemonClient::EmptyStopSystemTracingCallback() {
    546   return base::Bind(&EmptyStopSystemTracingCallbackBody);
    547 }
    548 
    549 // static
    550 DebugDaemonClient* DebugDaemonClient::Create() {
    551   return new DebugDaemonClientImpl();
    552 }
    553 
    554 }  // namespace chromeos
    555