| // Copyright 2015 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "webservd/dbus_protocol_handler.h" |
| |
| #include <utility> |
| |
| #include <base/bind.h> |
| #include <brillo/dbus/async_event_sequencer.h> |
| #include <brillo/dbus/exported_object_manager.h> |
| |
| #include "webservd/dbus_request_handler.h" |
| #include "webservd/protocol_handler.h" |
| #include "webservd/request.h" |
| #include "webservd/server.h" |
| |
| using brillo::dbus_utils::AsyncEventSequencer; |
| using brillo::dbus_utils::DBusObject; |
| using brillo::dbus_utils::ExportedObjectManager; |
| |
| namespace webservd { |
| |
| DBusProtocolHandler::DBusProtocolHandler(ExportedObjectManager* object_manager, |
| const dbus::ObjectPath& object_path, |
| ProtocolHandler* protocol_handler, |
| Server* server) |
| : dbus_object_{new DBusObject{object_manager, object_manager->GetBus(), |
| object_path}}, |
| protocol_handler_{protocol_handler}, |
| server_{server} { |
| dbus_adaptor_.SetId(protocol_handler->GetID()); |
| dbus_adaptor_.SetName(protocol_handler->GetName()); |
| dbus_adaptor_.SetPort(protocol_handler->GetPort()); |
| dbus_adaptor_.SetProtocol(protocol_handler->GetProtocol()); |
| dbus_adaptor_.SetCertificateFingerprint( |
| protocol_handler->GetCertificateFingerprint()); |
| } |
| |
| DBusProtocolHandler::~DBusProtocolHandler() { |
| for (const auto& pair : dbus_service_data_) { |
| server_->GetBus()->UnlistenForServiceOwnerChange( |
| pair.first, pair.second.on_client_disconnected_callback); |
| } |
| } |
| |
| void DBusProtocolHandler::RegisterAsync( |
| const AsyncEventSequencer::CompletionAction& completion_callback) { |
| scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer()); |
| dbus_adaptor_.RegisterWithDBusObject(dbus_object_.get()); |
| dbus_object_->RegisterAsync( |
| sequencer->GetHandler("Failed exporting ProtocolHandler.", true)); |
| sequencer->OnAllTasksCompletedCall({completion_callback}); |
| } |
| |
| ExportedObjectManager* DBusProtocolHandler::GetObjectManager() const { |
| return dbus_object_->GetObjectManager().get(); |
| } |
| |
| bool DBusProtocolHandler::AddRequestHandler( |
| brillo::ErrorPtr* /* error */, |
| dbus::Message* message, |
| const std::string& in_url, |
| const std::string& in_method, |
| const std::string& in_service_name, |
| std::string* out_request_handler_id) { |
| auto p = dbus_service_data_.find(in_service_name); |
| if (p == dbus_service_data_.end()) { |
| DBusServiceData dbus_service_data; |
| dbus_service_data.owner = message->GetSender(); |
| dbus_service_data.handler_proxy.reset( |
| new RequestHandlerProxy{server_->GetBus(), in_service_name}); |
| dbus_service_data.on_client_disconnected_callback = |
| base::Bind(&DBusProtocolHandler::OnClientDisconnected, |
| weak_ptr_factory_.GetWeakPtr(), in_service_name); |
| server_->GetBus()->ListenForServiceOwnerChange( |
| in_service_name, dbus_service_data.on_client_disconnected_callback); |
| p = dbus_service_data_ |
| .emplace(in_service_name, std::move(dbus_service_data)) |
| .first; |
| } |
| std::unique_ptr<RequestHandlerInterface> handler{ |
| new DBusRequestHandler{server_, p->second.handler_proxy.get()}}; |
| std::string handler_id = protocol_handler_->AddRequestHandler( |
| in_url, in_method, std::move(handler)); |
| p->second.handler_ids.insert(handler_id); |
| handler_to_service_name_map_.emplace(handler_id, in_service_name); |
| |
| *out_request_handler_id = handler_id; |
| return true; |
| } |
| |
| bool DBusProtocolHandler::RemoveRequestHandler( |
| brillo::ErrorPtr* error, const std::string& in_handler_id) { |
| auto p = handler_to_service_name_map_.find(in_handler_id); |
| if (p == handler_to_service_name_map_.end()) { |
| brillo::Error::AddToPrintf( |
| error, FROM_HERE, brillo::errors::dbus::kDomain, DBUS_ERROR_FAILED, |
| "Handler with ID %s does not exist", in_handler_id.c_str()); |
| return false; |
| } |
| std::string service_name = p->second; |
| CHECK(protocol_handler_->RemoveRequestHandler(in_handler_id)); |
| DBusServiceData& dbus_service_data = dbus_service_data_[service_name]; |
| CHECK_EQ(1u, dbus_service_data.handler_ids.erase(in_handler_id)); |
| if (dbus_service_data.handler_ids.empty()) { |
| server_->GetBus()->UnlistenForServiceOwnerChange( |
| service_name, dbus_service_data.on_client_disconnected_callback); |
| dbus_service_data_.erase(service_name); |
| } |
| return true; |
| } |
| |
| void DBusProtocolHandler::OnClientDisconnected( |
| const std::string& service_name, const std::string& service_owner) { |
| // This method will be called when the client's D-Bus service owner has |
| // changed which could be either the client exiting (|service_owner| is empty) |
| // or is being replaced with another running instance. |
| // In either case, we need to remove the old client's handlers since the |
| // new client will register their own on start up anyway. |
| // However pay attention to the case where the service owner is the same as |
| // the sender, in which case we should not remove the handlers. This happens |
| // if the handling process claims D-Bus service after it registers request |
| // handlers with the web server. |
| auto p = dbus_service_data_.find(service_name); |
| if (p == dbus_service_data_.end() || p->second.owner == service_owner) |
| return; |
| |
| for (const std::string& handler_id : p->second.handler_ids) { |
| handler_to_service_name_map_.erase(handler_id); |
| protocol_handler_->RemoveRequestHandler(handler_id); |
| } |
| server_->GetBus()->UnlistenForServiceOwnerChange( |
| service_name, p->second.on_client_disconnected_callback); |
| dbus_service_data_.erase(p); |
| } |
| |
| bool DBusProtocolHandler::GetRequestFileData( |
| brillo::ErrorPtr* error, |
| const std::string& in_request_id, |
| int32_t in_file_id, |
| brillo::dbus_utils::FileDescriptor* out_contents) { |
| auto request = GetRequest(in_request_id, error); |
| if (!request) |
| return false; |
| |
| base::File file = request->GetFileData(in_file_id); |
| if (file.IsValid()) { |
| *out_contents = file.GetPlatformFile(); |
| return true; |
| } |
| |
| brillo::Error::AddToPrintf(error, FROM_HERE, brillo::errors::dbus::kDomain, |
| DBUS_ERROR_FAILED, |
| "File with ID %d does not exist", in_file_id); |
| return false; |
| } |
| |
| bool DBusProtocolHandler::CompleteRequest( |
| brillo::ErrorPtr* error, |
| const std::string& in_request_id, |
| int32_t in_status_code, |
| const std::vector<std::tuple<std::string, std::string>>& in_headers, |
| int64_t in_data_size, |
| brillo::dbus_utils::FileDescriptor* out_response_stream) { |
| auto request = GetRequest(in_request_id, error); |
| if (!request) |
| return false; |
| |
| base::File file = request->Complete(in_status_code, in_headers, in_data_size); |
| if (file.IsValid()) { |
| *out_response_stream = file.GetPlatformFile(); |
| return true; |
| } |
| brillo::Error::AddTo(error, FROM_HERE, brillo::errors::dbus::kDomain, |
| DBUS_ERROR_FAILED, "Response already received"); |
| return false; |
| } |
| |
| Request* DBusProtocolHandler::GetRequest(const std::string& request_id, |
| brillo::ErrorPtr* error) { |
| Request* request = protocol_handler_->GetRequest(request_id); |
| if (!request) { |
| brillo::Error::AddToPrintf(error, FROM_HERE, brillo::errors::dbus::kDomain, |
| DBUS_ERROR_FAILED, "Unknown request ID: %s", |
| request_id.c_str()); |
| } |
| return request; |
| } |
| |
| } // namespace webservd |