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