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 "base/threading/worker_pool.h"
     23 #include "chromeos/dbus/pipe_reader.h"
     24 #include "dbus/bus.h"
     25 #include "dbus/message.h"
     26 #include "dbus/object_path.h"
     27 #include "dbus/object_proxy.h"
     28 #include "third_party/cros_system_api/dbus/service_constants.h"
     29 
     30 namespace {
     31 
     32 // Used in DebugDaemonClient::EmptySystemStopTracingCallback().
     33 void EmptyStopSystemTracingCallbackBody(
     34   const scoped_refptr<base::RefCountedString>& unused_result) {
     35 }
     36 
     37 }  // namespace
     38 
     39 namespace chromeos {
     40 
     41 // The DebugDaemonClient implementation used in production.
     42 class DebugDaemonClientImpl : public DebugDaemonClient {
     43  public:
     44   DebugDaemonClientImpl() : debugdaemon_proxy_(NULL), weak_ptr_factory_(this) {}
     45 
     46   virtual ~DebugDaemonClientImpl() {}
     47 
     48   // DebugDaemonClient override.
     49   virtual void GetDebugLogs(base::File file,
     50                             const GetDebugLogsCallback& callback) OVERRIDE {
     51 
     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     base::WorkerPool::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                    base::Owned(file_descriptor),
     63                    callback),
     64         false);
     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(const StopSystemTracingCallback&
    220       callback) OVERRIDE {
    221     if (pipe_reader_ != NULL) {
    222       LOG(ERROR) << "Busy doing StopSystemTracing";
    223       return false;
    224     }
    225 
    226     scoped_refptr<base::TaskRunner> task_runner =
    227         base::WorkerPool::GetTaskRunner(true /* task_is_slow */);
    228 
    229     pipe_reader_.reset(new PipeReaderForString(
    230         task_runner,
    231         base::Bind(&DebugDaemonClientImpl::OnIOComplete,
    232                    weak_ptr_factory_.GetWeakPtr())));
    233 
    234     base::File pipe_write_end = pipe_reader_->StartIO();
    235     // Create dbus::FileDescriptor on the worker thread; on return we'll
    236     // issue the D-Bus request to stop tracing and collect results.
    237     base::PostTaskAndReplyWithResult(
    238         task_runner,
    239         FROM_HERE,
    240         base::Bind(
    241             &DebugDaemonClientImpl::CreateFileDescriptorToStopSystemTracing,
    242             base::Passed(&pipe_write_end)),
    243         base::Bind(
    244             &DebugDaemonClientImpl::OnCreateFileDescriptorRequestStopSystem,
    245             weak_ptr_factory_.GetWeakPtr(),
    246             callback));
    247     return true;
    248   }
    249 
    250   virtual void TestICMP(const std::string& ip_address,
    251                         const TestICMPCallback& callback) OVERRIDE {
    252     dbus::MethodCall method_call(debugd::kDebugdInterface,
    253                                  debugd::kTestICMP);
    254     dbus::MessageWriter writer(&method_call);
    255     writer.AppendString(ip_address);
    256     debugdaemon_proxy_->CallMethod(
    257         &method_call,
    258         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    259         base::Bind(&DebugDaemonClientImpl::OnTestICMP,
    260                    weak_ptr_factory_.GetWeakPtr(),
    261                    callback));
    262   }
    263 
    264   virtual void TestICMPWithOptions(
    265       const std::string& ip_address,
    266       const std::map<std::string, std::string>& options,
    267       const TestICMPCallback& callback) OVERRIDE {
    268     dbus::MethodCall method_call(debugd::kDebugdInterface,
    269                                  debugd::kTestICMPWithOptions);
    270     dbus::MessageWriter writer(&method_call);
    271     dbus::MessageWriter sub_writer(NULL);
    272     dbus::MessageWriter elem_writer(NULL);
    273 
    274     // Write the host.
    275     writer.AppendString(ip_address);
    276 
    277     // Write the options.
    278     writer.OpenArray("{ss}", &sub_writer);
    279     std::map<std::string, std::string>::const_iterator it;
    280     for (it = options.begin(); it != options.end(); ++it) {
    281       sub_writer.OpenDictEntry(&elem_writer);
    282       elem_writer.AppendString(it->first);
    283       elem_writer.AppendString(it->second);
    284       sub_writer.CloseContainer(&elem_writer);
    285     }
    286     writer.CloseContainer(&sub_writer);
    287 
    288     // Call the function.
    289     debugdaemon_proxy_->CallMethod(
    290         &method_call,
    291         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    292         base::Bind(&DebugDaemonClientImpl::OnTestICMP,
    293                    weak_ptr_factory_.GetWeakPtr(),
    294                    callback));
    295   }
    296 
    297   virtual void UploadCrashes() OVERRIDE {
    298     dbus::MethodCall method_call(debugd::kDebugdInterface,
    299                                  debugd::kUploadCrashes);
    300     debugdaemon_proxy_->CallMethod(
    301         &method_call,
    302         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    303         base::Bind(&DebugDaemonClientImpl::OnStartMethod,
    304                    weak_ptr_factory_.GetWeakPtr()));
    305   }
    306 
    307  protected:
    308   virtual void Init(dbus::Bus* bus) OVERRIDE {
    309     debugdaemon_proxy_ =
    310         bus->GetObjectProxy(debugd::kDebugdServiceName,
    311                             dbus::ObjectPath(debugd::kDebugdServicePath));
    312   }
    313 
    314  private:
    315   // Called when a CheckValidity response is received.
    316   void OnCheckValidityGetDebugLogs(dbus::FileDescriptor* file_descriptor,
    317                                    const GetDebugLogsCallback& callback) {
    318     // Issue the dbus request to get debug logs.
    319     dbus::MethodCall method_call(
    320         debugd::kDebugdInterface,
    321         debugd::kGetDebugLogs);
    322     dbus::MessageWriter writer(&method_call);
    323     writer.AppendFileDescriptor(*file_descriptor);
    324 
    325     debugdaemon_proxy_->CallMethod(
    326         &method_call,
    327         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    328         base::Bind(&DebugDaemonClientImpl::OnGetDebugLogs,
    329                    weak_ptr_factory_.GetWeakPtr(),
    330                    callback));
    331   }
    332 
    333   // Called when a response for GetDebugLogs() is received.
    334   void OnGetDebugLogs(const GetDebugLogsCallback& callback,
    335                       dbus::Response* response) {
    336     if (!response) {
    337       LOG(ERROR) << "Failed to get debug logs";
    338       callback.Run(false);
    339       return;
    340     }
    341     callback.Run(true);
    342   }
    343 
    344   // Called when a response for SetDebugMode() is received.
    345   void OnSetDebugMode(const SetDebugModeCallback& callback,
    346                       dbus::Response* response) {
    347     if (!response) {
    348       LOG(ERROR) << "Failed to change debug mode";
    349       callback.Run(false);
    350     } else {
    351       callback.Run(true);
    352     }
    353   }
    354 
    355   void OnGetRoutes(const GetRoutesCallback& callback,
    356                    dbus::Response* response) {
    357     std::vector<std::string> routes;
    358     if (response) {
    359       dbus::MessageReader reader(response);
    360       if (reader.PopArrayOfStrings(&routes)) {
    361         callback.Run(true, routes);
    362       } else {
    363         LOG(ERROR) << "Got non-array response from GetRoutes";
    364         callback.Run(false, routes);
    365       }
    366     } else {
    367       callback.Run(false, routes);
    368     }
    369   }
    370 
    371   void OnGetNetworkStatus(const GetNetworkStatusCallback& callback,
    372                           dbus::Response* response) {
    373     std::string status;
    374     if (response && dbus::MessageReader(response).PopString(&status))
    375       callback.Run(true, status);
    376     else
    377       callback.Run(false, "");
    378   }
    379 
    380   void OnGetModemStatus(const GetModemStatusCallback& callback,
    381                         dbus::Response* response) {
    382     std::string status;
    383     if (response && dbus::MessageReader(response).PopString(&status))
    384       callback.Run(true, status);
    385     else
    386       callback.Run(false, "");
    387   }
    388 
    389   void OnGetWiMaxStatus(const GetWiMaxStatusCallback& callback,
    390                         dbus::Response* response) {
    391     std::string status;
    392     if (response && dbus::MessageReader(response).PopString(&status))
    393       callback.Run(true, status);
    394     else
    395       callback.Run(false, "");
    396   }
    397 
    398   void OnGetNetworkInterfaces(const GetNetworkInterfacesCallback& callback,
    399                               dbus::Response* response) {
    400     std::string status;
    401     if (response && dbus::MessageReader(response).PopString(&status))
    402       callback.Run(true, status);
    403     else
    404       callback.Run(false, "");
    405   }
    406 
    407   void OnGetPerfData(const GetPerfDataCallback& callback,
    408                      dbus::Response* response) {
    409     std::vector<uint8> data;
    410 
    411     if (!response) {
    412       return;
    413     }
    414 
    415     dbus::MessageReader reader(response);
    416     const uint8* buffer = NULL;
    417     size_t buf_size = 0;
    418     if (!reader.PopArrayOfBytes(&buffer, &buf_size))
    419       return;
    420 
    421     // TODO(asharif): Figure out a way to avoid this copy.
    422     data.insert(data.end(), buffer, buffer + buf_size);
    423 
    424     callback.Run(data);
    425   }
    426 
    427   void OnGetAllLogs(const GetLogsCallback& callback,
    428                     dbus::Response* response) {
    429     std::map<std::string, std::string> logs;
    430     bool broken = false; // did we see a broken (k,v) pair?
    431     dbus::MessageReader sub_reader(NULL);
    432     if (!response || !dbus::MessageReader(response).PopArray(&sub_reader)) {
    433       callback.Run(false, logs);
    434       return;
    435     }
    436     while (sub_reader.HasMoreData()) {
    437       dbus::MessageReader sub_sub_reader(NULL);
    438       std::string key, value;
    439       if (!sub_reader.PopDictEntry(&sub_sub_reader)
    440           || !sub_sub_reader.PopString(&key)
    441           || !sub_sub_reader.PopString(&value)) {
    442         broken = true;
    443         break;
    444       }
    445       logs[key] = value;
    446     }
    447     callback.Run(!sub_reader.HasMoreData() && !broken, logs);
    448   }
    449 
    450   void OnGetUserLogFiles(const GetLogsCallback& callback,
    451                          dbus::Response* response) {
    452     return OnGetAllLogs(callback, response);
    453   }
    454 
    455   // Called when a response for a simple start is received.
    456   void OnStartMethod(dbus::Response* response) {
    457     if (!response) {
    458       LOG(ERROR) << "Failed to request start";
    459       return;
    460     }
    461   }
    462 
    463   // Creates dbus::FileDescriptor from base::File.
    464   static scoped_ptr<dbus::FileDescriptor>
    465   CreateFileDescriptorToStopSystemTracing(base::File pipe_write_end) {
    466     if (!pipe_write_end.IsValid()) {
    467       LOG(ERROR) << "Cannot create pipe reader";
    468       // NB: continue anyway to shutdown tracing; toss trace data
    469       pipe_write_end.Initialize(base::FilePath(FILE_PATH_LITERAL("/dev/null")),
    470                                 base::File::FLAG_OPEN | base::File::FLAG_WRITE);
    471       // TODO(sleffler) if this fails AppendFileDescriptor will abort
    472     }
    473     scoped_ptr<dbus::FileDescriptor> file_descriptor(new dbus::FileDescriptor);
    474     file_descriptor->PutValue(pipe_write_end.TakePlatformFile());
    475     file_descriptor->CheckValidity();
    476     return file_descriptor.Pass();
    477   }
    478 
    479   // Called when a CheckValidity response is received.
    480   void OnCreateFileDescriptorRequestStopSystem(
    481       const StopSystemTracingCallback& callback,
    482       scoped_ptr<dbus::FileDescriptor> file_descriptor) {
    483     DCHECK(file_descriptor);
    484 
    485     // Issue the dbus request to stop system tracing
    486     dbus::MethodCall method_call(
    487         debugd::kDebugdInterface,
    488         debugd::kSystraceStop);
    489     dbus::MessageWriter writer(&method_call);
    490     writer.AppendFileDescriptor(*file_descriptor);
    491 
    492     callback_ = callback;
    493 
    494     DVLOG(1) << "Requesting a systrace stop";
    495     debugdaemon_proxy_->CallMethod(
    496         &method_call,
    497         dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
    498         base::Bind(&DebugDaemonClientImpl::OnRequestStopSystemTracing,
    499                    weak_ptr_factory_.GetWeakPtr()));
    500   }
    501 
    502   // Called when a response for RequestStopSystemTracing() is received.
    503   void OnRequestStopSystemTracing(dbus::Response* response) {
    504     if (!response) {
    505       LOG(ERROR) << "Failed to request systrace stop";
    506       // If debugd crashes or completes I/O before this message is processed
    507       // then pipe_reader_ can be NULL, see OnIOComplete().
    508       if (pipe_reader_.get())
    509         pipe_reader_->OnDataReady(-1); // terminate data stream
    510     }
    511     // NB: requester is signaled when i/o completes
    512   }
    513 
    514   void OnTestICMP(const TestICMPCallback& callback, dbus::Response* response) {
    515     std::string status;
    516     if (response && dbus::MessageReader(response).PopString(&status))
    517       callback.Run(true, status);
    518     else
    519       callback.Run(false, "");
    520   }
    521 
    522   // Called when pipe i/o completes; pass data on and delete the instance.
    523   void OnIOComplete() {
    524     std::string pipe_data;
    525     pipe_reader_->GetData(&pipe_data);
    526     callback_.Run(base::RefCountedString::TakeString(&pipe_data));
    527     pipe_reader_.reset();
    528   }
    529 
    530   dbus::ObjectProxy* debugdaemon_proxy_;
    531   scoped_ptr<PipeReaderForString> pipe_reader_;
    532   StopSystemTracingCallback callback_;
    533   base::WeakPtrFactory<DebugDaemonClientImpl> weak_ptr_factory_;
    534 
    535   DISALLOW_COPY_AND_ASSIGN(DebugDaemonClientImpl);
    536 };
    537 
    538 DebugDaemonClient::DebugDaemonClient() {
    539 }
    540 
    541 DebugDaemonClient::~DebugDaemonClient() {
    542 }
    543 
    544 // static
    545 DebugDaemonClient::StopSystemTracingCallback
    546 DebugDaemonClient::EmptyStopSystemTracingCallback() {
    547   return base::Bind(&EmptyStopSystemTracingCallbackBody);
    548 }
    549 
    550 // static
    551 DebugDaemonClient* DebugDaemonClient::Create() {
    552   return new DebugDaemonClientImpl();
    553 }
    554 
    555 }  // namespace chromeos
    556