1 : // Copyright 2014 Google Inc. All Rights Reserved.
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 "syzygy/kasko/service_bridge.h"
16 :
17 : #include <windows.h> // NOLINT
18 : #include <rpc.h>
19 : #include <stdint.h>
20 :
21 : #include "base/logging.h"
22 : #include "base/process/process_handle.h"
23 : #include "syzygy/common/com_utils.h"
24 : #include "syzygy/common/rpc/helpers.h"
25 : #include "syzygy/kasko/minidump_request.h"
26 : #include "syzygy/kasko/service.h"
27 :
28 : namespace kasko {
29 : namespace {
30 : ServiceBridge* g_service_bridge = NULL;
31 : } // namespace
32 : } // namespace kasko
33 :
34 : // RPC calls all come through this single free function. We use the singleton
35 : // g_service_bridge to forward the call to the running Service.
36 : boolean KaskoService_SendDiagnosticReport(handle_t IDL_handle,
37 E : MinidumpRequest request) {
38 E : DCHECK(kasko::g_service_bridge);
39 :
40 : base::ProcessId client_process_id =
41 E : ::common::rpc::GetClientProcessID(IDL_handle);
42 E : if (!client_process_id)
43 i : return false;
44 :
45 E : kasko::MinidumpRequest internal_request;
46 :
47 E : internal_request.client_exception_pointers = true;
48 E : internal_request.exception_info_address = request.exception_info_address;
49 :
50 E : switch (request.type) {
51 : case SMALL_DUMP:
52 E : internal_request.type = kasko::MinidumpRequest::SMALL_DUMP_TYPE;
53 E : break;
54 : case LARGER_DUMP:
55 E : internal_request.type = kasko::MinidumpRequest::LARGER_DUMP_TYPE;
56 E : break;
57 : case FULL_DUMP:
58 E : internal_request.type = kasko::MinidumpRequest::FULL_DUMP_TYPE;
59 E : break;
60 : default:
61 i : NOTREACHED();
62 : break;
63 : }
64 :
65 E : for (unsigned long i = 0; i < request.user_selected_memory_ranges_size; ++i) {
66 : kasko::MinidumpRequest::MemoryRange internal_memory_range = {
67 : static_cast<uint32_t>(
68 : request.user_selected_memory_ranges[i].base_address),
69 E : static_cast<uint32_t>(request.user_selected_memory_ranges[i].length)};
70 : internal_request.user_selected_memory_ranges.push_back(
71 E : internal_memory_range);
72 E : }
73 :
74 E : for (unsigned long i = 0; i < request.crash_keys_size; ++i) {
75 E : if (!request.crash_keys[i].name || !request.crash_keys[i].value)
76 i : continue;
77 :
78 : internal_request.crash_keys.push_back(kasko::MinidumpRequest::CrashKey(
79 E : request.crash_keys[i].name, request.crash_keys[i].value));
80 E : }
81 :
82 E : for (unsigned long i = 0; i < request.custom_streams_size; ++i) {
83 E : if (!request.custom_streams[i].size)
84 i : continue;
85 : kasko::MinidumpRequest::CustomStream internal_custom_stream = {
86 : request.custom_streams[i].type,
87 : reinterpret_cast<const void*>(request.custom_streams[i].data),
88 E : request.custom_streams[i].size};
89 E : internal_request.custom_streams.push_back(internal_custom_stream);
90 E : }
91 :
92 : kasko::g_service_bridge->service_->SendDiagnosticReport(
93 E : client_process_id, request.thread_id, internal_request);
94 :
95 E : return true;
96 E : }
97 :
98 : namespace kasko {
99 :
100 : ServiceBridge::ServiceBridge(const base::string16& protocol,
101 : const base::string16& endpoint,
102 : scoped_ptr<Service> service)
103 : : protocol_(protocol),
104 : endpoint_(endpoint),
105 : service_(service.Pass()),
106 E : running_(false) {
107 : // It's a bad idea to have two instances stepping on each other's toes.
108 E : CHECK(!g_service_bridge);
109 :
110 E : DCHECK(!protocol_.empty());
111 E : DCHECK(!endpoint_.empty());
112 E : DCHECK(service_);
113 E : g_service_bridge = this;
114 E : }
115 :
116 E : ServiceBridge::~ServiceBridge() {
117 : // It's a bad idea to shut down without stopping the service. It's also a bad
118 : // idea to block unexpectedly in our destructor.
119 E : CHECK(!running_);
120 :
121 E : DCHECK_EQ(this, g_service_bridge);
122 E : g_service_bridge = NULL;
123 E : }
124 :
125 E : bool ServiceBridge::Run() {
126 E : if (running_) return true;
127 :
128 : RPC_STATUS status = ::RpcServerUseProtseqEp(
129 : common::rpc::AsRpcWstr(&protocol_[0]), RPC_C_LISTEN_MAX_CALLS_DEFAULT,
130 E : common::rpc::AsRpcWstr(&endpoint_[0]), NULL /* Security descriptor. */);
131 :
132 : // RPC_S_DUPLICATE_ENDPOINT seems to be possible if a previous instance has
133 : // already registered this protocol and endpoint. The end result is still that
134 : // the endpoint is properly configured for this protocol.
135 E : if (status != RPC_S_OK && status != RPC_S_DUPLICATE_ENDPOINT) {
136 E : LOG(ERROR) << "Failed to init RPC protocol: " << ::common::LogWe(status)
137 : << ".";
138 E : } else {
139 : scoped_ptr<common::rpc::ScopedRpcInterfaceRegistration>
140 : interface_registration(new common::rpc::ScopedRpcInterfaceRegistration(
141 E : KaskoService_Kasko_v1_0_s_ifspec));
142 :
143 E : if (interface_registration->status() == RPC_S_OK) {
144 : status = ::RpcServerListen(1, // Minimum number of handler threads.
145 E : RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
146 :
147 E : if (status != RPC_S_OK) {
148 i : LOG(ERROR) << "Failed to run RPC server: " << ::common::LogWe(status)
149 : << ".";
150 i : } else {
151 E : running_ = true;
152 E : interface_registration_ = interface_registration.Pass();
153 : }
154 : }
155 E : }
156 :
157 E : return running_;
158 E : }
159 :
160 E : void ServiceBridge::Stop() {
161 E : if (!running_) return;
162 :
163 : // This call prevents new requests from being accepted.
164 E : RPC_STATUS status = ::RpcMgmtStopServerListening(NULL);
165 E : if (status != RPC_S_OK) {
166 : // If this fails, we could end up servicing calls in a bad state.
167 i : LOG(FATAL) << "Failed to stop the RPC server: " << ::common::LogWe(status)
168 : << ".";
169 : }
170 :
171 : // This call will block until all active requests are completed.
172 E : status = ::RpcMgmtWaitServerListen();
173 E : if (status != RPC_S_OK) {
174 : // If this fails, we could end up servicing calls in a bad state.
175 i : LOG(FATAL) << "Failed to wait for RPC server shutdown: "
176 : << ::common::LogWe(status) << ".";
177 : }
178 :
179 E : interface_registration_.reset();
180 E : running_ = false;
181 E : }
182 :
183 : } // namespace kasko
|