| 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 |