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