1 // Copyright 2015 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "webservd/dbus_protocol_handler.h"
16
17 #include <base/bind.h>
18 #include <brillo/dbus/async_event_sequencer.h>
19 #include <brillo/dbus/exported_object_manager.h>
20
21 #include "webservd/dbus_request_handler.h"
22 #include "webservd/protocol_handler.h"
23 #include "webservd/request.h"
24 #include "webservd/server.h"
25
26 using brillo::dbus_utils::AsyncEventSequencer;
27 using brillo::dbus_utils::DBusObject;
28 using brillo::dbus_utils::ExportedObjectManager;
29
30 namespace webservd {
31
DBusProtocolHandler(ExportedObjectManager * object_manager,const dbus::ObjectPath & object_path,ProtocolHandler * protocol_handler,Server * server)32 DBusProtocolHandler::DBusProtocolHandler(
33 ExportedObjectManager* object_manager,
34 const dbus::ObjectPath& object_path,
35 ProtocolHandler* protocol_handler,
36 Server* server)
37 : dbus_object_{new DBusObject{object_manager, object_manager->GetBus(),
38 object_path}},
39 protocol_handler_{protocol_handler},
40 server_{server} {
41 dbus_adaptor_.SetId(protocol_handler->GetID());
42 dbus_adaptor_.SetName(protocol_handler->GetName());
43 dbus_adaptor_.SetPort(protocol_handler->GetPort());
44 dbus_adaptor_.SetProtocol(protocol_handler->GetProtocol());
45 dbus_adaptor_.SetCertificateFingerprint(
46 protocol_handler->GetCertificateFingerprint());
47 }
48
~DBusProtocolHandler()49 DBusProtocolHandler::~DBusProtocolHandler() {
50 for (const auto& pair : dbus_service_data_) {
51 server_->GetBus()->UnlistenForServiceOwnerChange(
52 pair.first, pair.second.on_client_disconnected_callback);
53 }
54 }
55
RegisterAsync(const AsyncEventSequencer::CompletionAction & completion_callback)56 void DBusProtocolHandler::RegisterAsync(
57 const AsyncEventSequencer::CompletionAction& completion_callback) {
58 scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
59 dbus_adaptor_.RegisterWithDBusObject(dbus_object_.get());
60 dbus_object_->RegisterAsync(
61 sequencer->GetHandler("Failed exporting ProtocolHandler.", true));
62 sequencer->OnAllTasksCompletedCall({completion_callback});
63 }
64
GetObjectManager() const65 ExportedObjectManager* DBusProtocolHandler::GetObjectManager() const {
66 return dbus_object_->GetObjectManager().get();
67 }
68
AddRequestHandler(brillo::ErrorPtr *,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)69 bool DBusProtocolHandler::AddRequestHandler(
70 brillo::ErrorPtr* /* error */,
71 dbus::Message* message,
72 const std::string& in_url,
73 const std::string& in_method,
74 const std::string& in_service_name,
75 std::string* out_request_handler_id) {
76 auto p = dbus_service_data_.find(in_service_name);
77 if (p == dbus_service_data_.end()) {
78 DBusServiceData dbus_service_data;
79 dbus_service_data.owner = message->GetSender();
80 dbus_service_data.handler_proxy.reset(
81 new RequestHandlerProxy{server_->GetBus(), in_service_name});
82 dbus_service_data.on_client_disconnected_callback =
83 base::Bind(&DBusProtocolHandler::OnClientDisconnected,
84 weak_ptr_factory_.GetWeakPtr(),
85 in_service_name);
86 server_->GetBus()->ListenForServiceOwnerChange(
87 in_service_name, dbus_service_data.on_client_disconnected_callback);
88 p = dbus_service_data_.emplace(in_service_name,
89 std::move(dbus_service_data)).first;
90 }
91 std::unique_ptr<RequestHandlerInterface> handler{
92 new DBusRequestHandler{server_, p->second.handler_proxy.get()}
93 };
94 std::string handler_id = protocol_handler_->AddRequestHandler(
95 in_url, in_method, std::move(handler));
96 p->second.handler_ids.insert(handler_id);
97 handler_to_service_name_map_.emplace(handler_id, in_service_name);
98
99 *out_request_handler_id = handler_id;
100 return true;
101 }
102
RemoveRequestHandler(brillo::ErrorPtr * error,const std::string & in_handler_id)103 bool DBusProtocolHandler::RemoveRequestHandler(
104 brillo::ErrorPtr* error,
105 const std::string& in_handler_id) {
106
107 auto p = handler_to_service_name_map_.find(in_handler_id);
108 if (p == handler_to_service_name_map_.end()) {
109 brillo::Error::AddToPrintf(error,
110 FROM_HERE,
111 brillo::errors::dbus::kDomain,
112 DBUS_ERROR_FAILED,
113 "Handler with ID %s does not exist",
114 in_handler_id.c_str());
115 return false;
116 }
117 std::string service_name = p->second;
118 CHECK(protocol_handler_->RemoveRequestHandler(in_handler_id));
119 DBusServiceData& dbus_service_data = dbus_service_data_[service_name];
120 CHECK_EQ(1u, dbus_service_data.handler_ids.erase(in_handler_id));
121 if (dbus_service_data.handler_ids.empty()) {
122 server_->GetBus()->UnlistenForServiceOwnerChange(
123 service_name, dbus_service_data.on_client_disconnected_callback);
124 dbus_service_data_.erase(service_name);
125 }
126 return true;
127 }
128
OnClientDisconnected(const std::string & service_name,const std::string & service_owner)129 void DBusProtocolHandler::OnClientDisconnected(
130 const std::string& service_name,
131 const std::string& service_owner) {
132 // This method will be called when the client's D-Bus service owner has
133 // changed which could be either the client exiting (|service_owner| is empty)
134 // or is being replaced with another running instance.
135 // In either case, we need to remove the old client's handlers since the
136 // new client will register their own on start up anyway.
137 // However pay attention to the case where the service owner is the same as
138 // the sender, in which case we should not remove the handlers. This happens
139 // if the handling process claims D-Bus service after it registers request
140 // handlers with the web server.
141 auto p = dbus_service_data_.find(service_name);
142 if (p == dbus_service_data_.end() || p->second.owner == service_owner)
143 return;
144
145 for (const std::string& handler_id : p->second.handler_ids) {
146 handler_to_service_name_map_.erase(handler_id);
147 protocol_handler_->RemoveRequestHandler(handler_id);
148 }
149 server_->GetBus()->UnlistenForServiceOwnerChange(
150 service_name, p->second.on_client_disconnected_callback);
151 dbus_service_data_.erase(p);
152 }
153
GetRequestFileData(brillo::ErrorPtr * error,const std::string & in_request_id,int32_t in_file_id,dbus::FileDescriptor * out_contents)154 bool DBusProtocolHandler::GetRequestFileData(
155 brillo::ErrorPtr* error,
156 const std::string& in_request_id,
157 int32_t in_file_id,
158 dbus::FileDescriptor* out_contents) {
159 auto request = GetRequest(in_request_id, error);
160 if (!request)
161 return false;
162
163 base::File file = request->GetFileData(in_file_id);
164 if (file.IsValid()) {
165 out_contents->PutValue(file.TakePlatformFile());
166 out_contents->CheckValidity();
167 return true;
168 }
169
170 brillo::Error::AddToPrintf(error,
171 FROM_HERE,
172 brillo::errors::dbus::kDomain,
173 DBUS_ERROR_FAILED,
174 "File with ID %d does not exist",
175 in_file_id);
176 return false;
177 }
178
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,dbus::FileDescriptor * out_response_stream)179 bool DBusProtocolHandler::CompleteRequest(
180 brillo::ErrorPtr* error,
181 const std::string& in_request_id,
182 int32_t in_status_code,
183 const std::vector<std::tuple<std::string, std::string>>& in_headers,
184 int64_t in_data_size,
185 dbus::FileDescriptor* out_response_stream) {
186 auto request = GetRequest(in_request_id, error);
187 if (!request)
188 return false;
189
190 base::File file = request->Complete(in_status_code, in_headers, in_data_size);
191 if (file.IsValid()) {
192 out_response_stream->PutValue(file.TakePlatformFile());
193 out_response_stream->CheckValidity();
194 return true;
195 }
196 brillo::Error::AddTo(error, FROM_HERE, brillo::errors::dbus::kDomain,
197 DBUS_ERROR_FAILED, "Response already received");
198 return false;
199 }
200
GetRequest(const std::string & request_id,brillo::ErrorPtr * error)201 Request* DBusProtocolHandler::GetRequest(const std::string& request_id,
202 brillo::ErrorPtr* error) {
203 Request* request = protocol_handler_->GetRequest(request_id);
204 if (!request) {
205 brillo::Error::AddToPrintf(error,
206 FROM_HERE,
207 brillo::errors::dbus::kDomain,
208 DBUS_ERROR_FAILED,
209 "Unknown request ID: %s",
210 request_id.c_str());
211 }
212 return request;
213 }
214
215 } // namespace webservd
216