OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
3 * Use of this source code is governed by a BSD-style license that can be | |
4 * found in the LICENSE file. | |
5 */ | |
6 | |
7 #define NACL_LOG_MODULE_NAME "Plugin_ServiceRuntime" | |
8 | |
9 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h" | |
10 | |
11 #include <string.h> | |
12 #include <string> | |
13 #include <utility> | |
14 | |
15 #include "base/compiler_specific.h" | |
16 | |
17 #include "native_client/src/include/portability_io.h" | |
18 #include "native_client/src/include/portability_string.h" | |
19 #include "native_client/src/include/nacl_macros.h" | |
20 #include "native_client/src/include/nacl_scoped_ptr.h" | |
21 #include "native_client/src/shared/platform/nacl_check.h" | |
22 #include "native_client/src/shared/platform/nacl_log.h" | |
23 #include "native_client/src/shared/platform/nacl_sync.h" | |
24 #include "native_client/src/shared/platform/nacl_sync_checked.h" | |
25 #include "native_client/src/shared/platform/nacl_sync_raii.h" | |
26 #include "native_client/src/trusted/nonnacl_util/sel_ldr_launcher.h" | |
27 | |
28 #include "native_client/src/public/imc_types.h" | |
29 #include "native_client/src/public/nacl_file_info.h" | |
30 #include "native_client/src/trusted/service_runtime/nacl_error_code.h" | |
31 | |
32 #include "ppapi/c/pp_errors.h" | |
33 #include "ppapi/cpp/core.h" | |
34 #include "ppapi/cpp/completion_callback.h" | |
35 | |
36 #include "ppapi/native_client/src/trusted/plugin/plugin.h" | |
37 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h" | |
38 #include "ppapi/native_client/src/trusted/plugin/pnacl_resources.h" | |
39 #include "ppapi/native_client/src/trusted/plugin/sel_ldr_launcher_chrome.h" | |
40 #include "ppapi/native_client/src/trusted/plugin/srpc_client.h" | |
41 #include "ppapi/native_client/src/trusted/plugin/utility.h" | |
42 | |
43 namespace plugin { | |
44 | |
45 ServiceRuntime::ServiceRuntime(Plugin* plugin, | |
46 PP_Instance pp_instance, | |
47 bool main_service_runtime, | |
48 bool uses_nonsfi_mode) | |
49 : plugin_(plugin), | |
50 pp_instance_(pp_instance), | |
51 main_service_runtime_(main_service_runtime), | |
52 uses_nonsfi_mode_(uses_nonsfi_mode), | |
53 start_sel_ldr_done_(false), | |
54 sel_ldr_wait_timed_out_(false), | |
55 start_nexe_done_(false), | |
56 nexe_started_ok_(false), | |
57 bootstrap_channel_(NACL_INVALID_HANDLE) { | |
58 NaClSrpcChannelInitialize(&command_channel_); | |
59 NaClXMutexCtor(&mu_); | |
60 NaClXCondVarCtor(&cond_); | |
61 } | |
62 | |
63 bool ServiceRuntime::SetupCommandChannel() { | |
64 NaClLog(4, "ServiceRuntime::SetupCommand (this=%p, subprocess=%p)\n", | |
65 static_cast<void*>(this), | |
66 static_cast<void*>(subprocess_.get())); | |
67 // Set up the bootstrap channel in our subprocess so that we can establish | |
68 // SRPC. | |
69 subprocess_->set_channel(bootstrap_channel_); | |
70 | |
71 if (uses_nonsfi_mode_) { | |
72 // In non-SFI mode, no SRPC is used. Just skips and returns success. | |
73 return true; | |
74 } | |
75 | |
76 if (!subprocess_->SetupCommand(&command_channel_)) { | |
77 ErrorInfo error_info; | |
78 error_info.SetReport(PP_NACL_ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL, | |
79 "ServiceRuntime: command channel creation failed"); | |
80 ReportLoadError(error_info); | |
81 return false; | |
82 } | |
83 return true; | |
84 } | |
85 | |
86 bool ServiceRuntime::StartModule() { | |
87 // start the module. otherwise we cannot connect for multimedia | |
88 // subsystem since that is handled by user-level code (not secure!) | |
89 // in libsrpc. | |
90 int load_status = -1; | |
91 if (uses_nonsfi_mode_) { | |
92 // In non-SFI mode, we don't need to call start_module SRPC to launch | |
93 // the plugin. | |
94 load_status = LOAD_OK; | |
95 } else { | |
96 NaClSrpcResultCodes rpc_result = | |
97 NaClSrpcInvokeBySignature(&command_channel_, | |
98 "start_module::i", | |
99 &load_status); | |
100 | |
101 if (NACL_SRPC_RESULT_OK != rpc_result) { | |
102 ErrorInfo error_info; | |
103 error_info.SetReport(PP_NACL_ERROR_SEL_LDR_START_MODULE, | |
104 "ServiceRuntime: could not start nacl module"); | |
105 ReportLoadError(error_info); | |
106 return false; | |
107 } | |
108 } | |
109 | |
110 NaClLog(4, "ServiceRuntime::StartModule (load_status=%d)\n", load_status); | |
111 if (main_service_runtime_) { | |
112 if (load_status < 0 || load_status > NACL_ERROR_CODE_MAX) | |
113 load_status = LOAD_STATUS_UNKNOWN; | |
114 GetNaClInterface()->ReportSelLdrStatus(pp_instance_, | |
115 load_status, | |
116 NACL_ERROR_CODE_MAX); | |
117 } | |
118 | |
119 if (LOAD_OK != load_status) { | |
120 ErrorInfo error_info; | |
121 error_info.SetReport( | |
122 PP_NACL_ERROR_SEL_LDR_START_STATUS, | |
123 NaClErrorString(static_cast<NaClErrorCode>(load_status))); | |
124 ReportLoadError(error_info); | |
125 return false; | |
126 } | |
127 return true; | |
128 } | |
129 | |
130 void ServiceRuntime::StartSelLdr(const SelLdrStartParams& params, | |
131 pp::CompletionCallback callback) { | |
132 NaClLog(4, "ServiceRuntime::Start\n"); | |
133 | |
134 nacl::scoped_ptr<SelLdrLauncherChrome> | |
135 tmp_subprocess(new SelLdrLauncherChrome()); | |
136 if (NULL == tmp_subprocess.get()) { | |
137 NaClLog(LOG_ERROR, "ServiceRuntime::Start (subprocess create failed)\n"); | |
138 ErrorInfo error_info; | |
139 error_info.SetReport( | |
140 PP_NACL_ERROR_SEL_LDR_CREATE_LAUNCHER, | |
141 "ServiceRuntime: failed to create sel_ldr launcher"); | |
142 ReportLoadError(error_info); | |
143 pp::Module::Get()->core()->CallOnMainThread(0, callback, PP_ERROR_FAILED); | |
144 return; | |
145 } | |
146 | |
147 GetNaClInterface()->LaunchSelLdr( | |
148 pp_instance_, | |
149 PP_FromBool(main_service_runtime_), | |
150 params.url.c_str(), | |
151 ¶ms.file_info, | |
152 PP_FromBool(uses_nonsfi_mode_), | |
153 params.process_type, | |
154 &bootstrap_channel_, | |
155 callback.pp_completion_callback()); | |
156 subprocess_.reset(tmp_subprocess.release()); | |
157 } | |
158 | |
159 bool ServiceRuntime::WaitForSelLdrStart() { | |
160 // Time to wait on condvar (for browser to create a new sel_ldr process on | |
161 // our behalf). Use 6 seconds to be *fairly* conservative. | |
162 // | |
163 // On surfaway, the CallOnMainThread above may never get scheduled | |
164 // to unblock this condvar, or the IPC reply from the browser to renderer | |
165 // might get canceled/dropped. However, it is currently important to | |
166 // avoid waiting indefinitely because ~PnaclCoordinator will attempt to | |
167 // join() the PnaclTranslateThread, and the PnaclTranslateThread is waiting | |
168 // for the signal before exiting. | |
169 static int64_t const kWaitTimeMicrosecs = 6 * NACL_MICROS_PER_UNIT; | |
170 int64_t left_to_wait = kWaitTimeMicrosecs; | |
171 int64_t deadline = NaClGetTimeOfDayMicroseconds() + left_to_wait; | |
172 nacl::MutexLocker take(&mu_); | |
173 while(!start_sel_ldr_done_ && left_to_wait > 0) { | |
174 struct nacl_abi_timespec left_timespec; | |
175 left_timespec.tv_sec = left_to_wait / NACL_MICROS_PER_UNIT; | |
176 left_timespec.tv_nsec = | |
177 (left_to_wait % NACL_MICROS_PER_UNIT) * NACL_NANOS_PER_MICRO; | |
178 NaClXCondVarTimedWaitRelative(&cond_, &mu_, &left_timespec); | |
179 int64_t now = NaClGetTimeOfDayMicroseconds(); | |
180 left_to_wait = deadline - now; | |
181 } | |
182 if (left_to_wait <= 0) | |
183 sel_ldr_wait_timed_out_ = true; | |
184 return start_sel_ldr_done_; | |
185 } | |
186 | |
187 void ServiceRuntime::SignalStartSelLdrDone() { | |
188 nacl::MutexLocker take(&mu_); | |
189 start_sel_ldr_done_ = true; | |
190 NaClXCondVarSignal(&cond_); | |
191 } | |
192 | |
193 bool ServiceRuntime::SelLdrWaitTimedOut() { | |
194 nacl::MutexLocker take(&mu_); | |
195 return sel_ldr_wait_timed_out_; | |
196 } | |
197 | |
198 bool ServiceRuntime::WaitForNexeStart() { | |
199 nacl::MutexLocker take(&mu_); | |
200 while (!start_nexe_done_) | |
201 NaClXCondVarWait(&cond_, &mu_); | |
202 return nexe_started_ok_; | |
203 } | |
204 | |
205 void ServiceRuntime::SignalNexeStarted(bool ok) { | |
206 nacl::MutexLocker take(&mu_); | |
207 start_nexe_done_ = true; | |
208 nexe_started_ok_ = ok; | |
209 NaClXCondVarSignal(&cond_); | |
210 } | |
211 | |
212 void ServiceRuntime::StartNexe() { | |
213 bool ok = StartNexeInternal(); | |
214 if (ok) { | |
215 NaClLog(4, "ServiceRuntime::StartNexe (success)\n"); | |
216 } else { | |
217 ReapLogs(); | |
218 } | |
219 // This only matters if a background thread is waiting, but we signal in all | |
220 // cases to simplify the code. | |
221 SignalNexeStarted(ok); | |
222 } | |
223 | |
224 bool ServiceRuntime::StartNexeInternal() { | |
225 if (!SetupCommandChannel()) | |
226 return false; | |
227 return StartModule(); | |
228 } | |
229 | |
230 void ServiceRuntime::ReapLogs() { | |
231 // TODO(teravest): We should allow the NaCl process to crash itself when a | |
232 // module fails to start, and remove the call to RemoteLog() here. The | |
233 // reverse channel is no longer needed for crash reporting. | |
234 // | |
235 // The reasoning behind the current code behavior follows: | |
236 // On a load failure the NaCl process does not crash itself to | |
237 // avoid a race where the no-more-senders error on the reverse | |
238 // channel service thread might cause the crash-detection logic to | |
239 // kick in before the start_module RPC reply has been received. So | |
240 // we induce a NaCl process crash here. | |
241 RemoteLog(LOG_FATAL, "reap logs\n"); | |
242 | |
243 // TODO(teravest): Release subprocess_ here since it's no longer needed. It | |
244 // was previously kept around to collect crash log output from the bootstrap | |
245 // channel. | |
246 } | |
247 | |
248 void ServiceRuntime::ReportLoadError(const ErrorInfo& error_info) { | |
249 if (main_service_runtime_) { | |
250 plugin_->ReportLoadError(error_info); | |
251 } | |
252 } | |
253 | |
254 SrpcClient* ServiceRuntime::SetupAppChannel() { | |
255 NaClLog(4, "ServiceRuntime::SetupAppChannel (subprocess_=%p)\n", | |
256 reinterpret_cast<void*>(subprocess_.get())); | |
257 nacl::DescWrapper* connect_desc = subprocess_->socket_addr()->Connect(); | |
258 if (NULL == connect_desc) { | |
259 NaClLog(LOG_ERROR, "ServiceRuntime::SetupAppChannel (connect failed)\n"); | |
260 return NULL; | |
261 } else { | |
262 NaClLog(4, "ServiceRuntime::SetupAppChannel (conect_desc=%p)\n", | |
263 static_cast<void*>(connect_desc)); | |
264 SrpcClient* srpc_client = SrpcClient::New(connect_desc); | |
265 NaClLog(4, "ServiceRuntime::SetupAppChannel (srpc_client=%p)\n", | |
266 static_cast<void*>(srpc_client)); | |
267 delete connect_desc; | |
268 return srpc_client; | |
269 } | |
270 } | |
271 | |
272 bool ServiceRuntime::RemoteLog(int severity, const std::string& msg) { | |
273 NaClSrpcResultCodes rpc_result = | |
274 NaClSrpcInvokeBySignature(&command_channel_, | |
275 "log:is:", | |
276 severity, | |
277 strdup(msg.c_str())); | |
278 return (NACL_SRPC_RESULT_OK == rpc_result); | |
279 } | |
280 | |
281 void ServiceRuntime::Shutdown() { | |
282 // Abandon callbacks, tell service threads to quit if they were | |
283 // blocked waiting for main thread operations to finish. Note that | |
284 // some callbacks must still await their completion event, e.g., | |
285 // CallOnMainThread must still wait for the time out, or I/O events | |
286 // must finish, so resources associated with pending events cannot | |
287 // be deallocated. | |
288 | |
289 // Note that this does waitpid() to get rid of any zombie subprocess. | |
290 subprocess_.reset(NULL); | |
291 | |
292 NaClSrpcDtor(&command_channel_); | |
293 } | |
294 | |
295 ServiceRuntime::~ServiceRuntime() { | |
296 NaClLog(4, "ServiceRuntime::~ServiceRuntime (this=%p)\n", | |
297 static_cast<void*>(this)); | |
298 // We do this just in case Shutdown() was not called. | |
299 subprocess_.reset(NULL); | |
300 | |
301 NaClCondVarDtor(&cond_); | |
302 NaClMutexDtor(&mu_); | |
303 } | |
304 | |
305 } // namespace plugin | |
OLD | NEW |