1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <errno.h> 6 #include <fcntl.h> 7 #include <pthread.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <sys/ioctl.h> 11 #include <sys/types.h> 12 #include <sys/stat.h> 13 #include <termios.h> 14 15 #include <algorithm> 16 #include <cstdlib> 17 #include <cstring> 18 #include <sstream> 19 #include <string> 20 #include <vector> 21 22 #include "nacl_io/ioctl.h" 23 #include "nacl_io/kernel_wrap.h" 24 #include "nacl_io/log.h" 25 #include "nacl_io/nacl_io.h" 26 27 #include "ppapi/c/ppb_var.h" 28 29 #include "ppapi/cpp/input_event.h" 30 #include "ppapi/cpp/message_loop.h" 31 #include "ppapi/cpp/module.h" 32 #include "ppapi/cpp/rect.h" 33 #include "ppapi/cpp/size.h" 34 #include "ppapi/cpp/touch_point.h" 35 #include "ppapi/cpp/var.h" 36 #include "ppapi/cpp/var_array.h" 37 #include "ppapi/cpp/var_dictionary.h" 38 39 #include "ppapi_simple/ps_event.h" 40 #include "ppapi_simple/ps_instance.h" 41 #include "ppapi_simple/ps_interface.h" 42 #include "ppapi_simple/ps_main.h" 43 44 #if defined(WIN32) 45 #define open _open 46 #define dup2 _dup2 47 #endif 48 49 static PSInstance* s_InstanceObject = NULL; 50 51 PSInstance* PSInstance::GetInstance() { 52 return s_InstanceObject; 53 } 54 55 struct StartInfo { 56 PSInstance* inst_; 57 uint32_t argc_; 58 char** argv_; 59 }; 60 61 62 // The starting point for 'main'. We create this thread to hide the real 63 // main pepper thread which must never be blocked. 64 void* PSInstance::MainThreadThunk(void *info) { 65 s_InstanceObject->Trace("Got MainThreadThunk.\n"); 66 StartInfo* si = static_cast<StartInfo*>(info); 67 PSInstance* instance = si->inst_; 68 instance->main_loop_ = new pp::MessageLoop(si->inst_); 69 instance->main_loop_->AttachToCurrentThread(); 70 71 int ret = instance->MainThread(si->argc_, si->argv_); 72 73 // Clean up StartInfo. 74 for (uint32_t i = 0; i < si->argc_; i++) { 75 delete[] si->argv_[i]; 76 } 77 delete[] si->argv_; 78 delete si; 79 80 // Exit the entire process once the 'main' thread returns. 81 // The error code will be available to javascript via 82 // the exitcode parameter of the crash event. 83 #ifdef __native_client__ 84 exit(ret); 85 #else 86 instance->ExitHandshake(ret); 87 #endif 88 return NULL; 89 } 90 91 void PSInstance::ExitHandshake(int status) { 92 if (exit_message_ == NULL) 93 return; 94 95 RegisterMessageHandler(exit_message_, MessageHandlerExitStatic, this); 96 97 // Send the exit message to JavaScript. Then wait 98 // for the reply/confirmation. 99 std::stringstream ss; 100 ss << exit_message_ << ":" << status; 101 102 pthread_mutex_lock(&exit_lock_); 103 PostMessage(ss.str()); 104 pthread_cond_wait(&exit_cond_, &exit_lock_); 105 pthread_mutex_unlock(&exit_lock_); 106 } 107 108 // The default implementation supports running a 'C' main. 109 int PSInstance::MainThread(int argc, char* argv[]) { 110 if (!main_cb_) { 111 Error("No main defined.\n"); 112 return 0; 113 } 114 115 Trace("Starting MAIN.\n"); 116 int ret = main_cb_(argc, argv); 117 Log("Main thread returned with %d.\n", ret); 118 119 return ret; 120 } 121 122 PSInstance::PSInstance(PP_Instance instance) 123 : pp::Instance(instance), 124 pp::MouseLock(this), 125 pp::Graphics3DClient(this), 126 main_loop_(NULL), 127 events_enabled_(PSE_NONE), 128 verbosity_(PSV_WARN), 129 tty_fd_(-1), 130 tty_prefix_(NULL), 131 exit_message_(NULL) { 132 133 pthread_mutex_init(&exit_lock_, NULL); 134 pthread_cond_init(&exit_cond_, NULL); 135 136 // Set the single Instance object 137 s_InstanceObject = this; 138 139 #ifdef NACL_SDK_DEBUG 140 SetVerbosity(PSV_LOG); 141 #endif 142 143 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | 144 PP_INPUTEVENT_CLASS_KEYBOARD | 145 PP_INPUTEVENT_CLASS_WHEEL | 146 PP_INPUTEVENT_CLASS_TOUCH); 147 } 148 149 PSInstance::~PSInstance() { 150 s_InstanceObject = NULL; 151 } 152 153 void PSInstance::SetMain(PSMainFunc_t main) { 154 main_cb_ = main; 155 } 156 157 bool PSInstance::Init(uint32_t arg, 158 const char* argn[], 159 const char* argv[]) { 160 StartInfo* si = new StartInfo; 161 162 si->inst_ = this; 163 si->argc_ = 0; 164 si->argv_ = new char *[arg+1]; 165 si->argv_[0] = NULL; 166 167 // Process embed attributes into the environment. 168 // Converted the attribute names to uppercase as environment variables are 169 // case sensitive but are almost universally uppercase in practice. 170 for (uint32_t i = 0; i < arg; i++) { 171 std::string key = argn[i]; 172 std::transform(key.begin(), key.end(), key.begin(), toupper); 173 setenv(key.c_str(), argv[i], 1); 174 } 175 176 // Set a default value for SRC. 177 setenv("SRC", "NMF?", 0); 178 // Use the src tag name if ARG0 is not explicitly specified. 179 setenv("ARG0", getenv("SRC"), 0); 180 181 // Walk ARG0..ARGn populating argv until an argument is missing. 182 for (;;) { 183 std::ostringstream arg_stream; 184 arg_stream << "ARG" << si->argc_; 185 std::string arg_name = arg_stream.str(); 186 const char* next_arg = getenv(arg_name.c_str()); 187 if (NULL == next_arg) 188 break; 189 190 char* value = new char[strlen(next_arg) + 1]; 191 strcpy(value, next_arg); 192 si->argv_[si->argc_++] = value; 193 } 194 195 PSInterfaceInit(); 196 bool props_processed = ProcessProperties(); 197 198 // Log arg values only once ProcessProperties has been 199 // called so that the ps_verbosity attribute will be in 200 // effect. 201 for (uint32_t i = 0; i < arg; i++) { 202 if (argv[i]) { 203 Trace("attribs[%d] '%s=%s'\n", i, argn[i], argv[i]); 204 } else { 205 Trace("attribs[%d] '%s'\n", i, argn[i]); 206 } 207 } 208 209 for (uint32_t i = 0; i < si->argc_; i++) { 210 Trace("argv[%d] '%s'\n", i, si->argv_[i]); 211 } 212 213 if (!props_processed) { 214 Warn("Skipping create thread.\n"); 215 return false; 216 } 217 218 pthread_t main_thread; 219 int ret = pthread_create(&main_thread, NULL, MainThreadThunk, si); 220 Trace("Created thread: %d.\n", ret); 221 return ret == 0; 222 } 223 224 // Processes the properties set at compile time via the 225 // initialization macro, or via dynamically set embed attributes 226 // through instance DidCreate. 227 bool PSInstance::ProcessProperties() { 228 // Reset verbosity if passed in 229 const char* verbosity = getenv("PS_VERBOSITY"); 230 if (verbosity) SetVerbosity(static_cast<Verbosity>(atoi(verbosity))); 231 232 // Enable NaCl IO to map STDIN, STDOUT, and STDERR 233 nacl_io_init_ppapi(PSGetInstanceId(), PSGetInterface); 234 235 // Set default values 236 setenv("PS_STDIN", "/dev/stdin", 0); 237 setenv("PS_STDOUT", "/dev/stdout", 0); 238 setenv("PS_STDERR", "/dev/console3", 0); 239 240 int fd0 = open(getenv("PS_STDIN"), O_RDONLY); 241 dup2(fd0, 0); 242 243 int fd1 = open(getenv("PS_STDOUT"), O_WRONLY); 244 dup2(fd1, 1); 245 246 int fd2 = open(getenv("PS_STDERR"), O_WRONLY); 247 dup2(fd2, 2); 248 249 tty_prefix_ = getenv("PS_TTY_PREFIX"); 250 if (tty_prefix_) { 251 tty_fd_ = open("/dev/tty", O_WRONLY); 252 if (tty_fd_ >= 0) { 253 RegisterMessageHandler(tty_prefix_, MessageHandlerInputStatic, this); 254 const char* tty_resize = getenv("PS_TTY_RESIZE"); 255 if (tty_resize) 256 RegisterMessageHandler(tty_resize, MessageHandlerResizeStatic, this); 257 258 char* tty_rows = getenv("PS_TTY_ROWS"); 259 char* tty_cols = getenv("PS_TTY_COLS"); 260 if (tty_rows && tty_cols) { 261 char* end = tty_rows; 262 int rows = strtol(tty_rows, &end, 10); 263 if (*end != '\0' || rows < 0) { 264 Error("Invalid value for PS_TTY_ROWS: %s", tty_rows); 265 } else { 266 end = tty_cols; 267 int cols = strtol(tty_cols, &end, 10); 268 if (*end != '\0' || cols < 0) 269 Error("Invalid value for PS_TTY_COLS: %s", tty_cols); 270 else 271 HandleResize(cols, rows); 272 } 273 } 274 else if (tty_rows || tty_cols) { 275 Error("PS_TTY_ROWS and PS_TTY_COLS must be set together"); 276 } 277 278 tioc_nacl_output handler; 279 handler.handler = TtyOutputHandlerStatic; 280 handler.user_data = this; 281 ioctl(tty_fd_, TIOCNACLOUTPUT, &handler); 282 } else { 283 Error("Failed to open /dev/tty.\n"); 284 } 285 } 286 287 RegisterMessageHandler("jspipe1", MessageHandlerInputStatic, this); 288 RegisterMessageHandler("jspipe2", MessageHandlerInputStatic, this); 289 RegisterMessageHandler("jspipe3", MessageHandlerInputStatic, this); 290 291 exit_message_ = getenv("PS_EXIT_MESSAGE"); 292 293 // If PS_EXIT_MESSAGE is set in the environment then we perform a handshake 294 // with JavaScript when program exits. 295 if (exit_message_ != NULL) 296 nacl_io_set_exit_callback(HandleExitStatic, this); 297 298 // Set line buffering on stdout and stderr 299 #if !defined(WIN32) 300 setvbuf(stderr, NULL, _IOLBF, 0); 301 setvbuf(stdout, NULL, _IOLBF, 0); 302 #endif 303 return true; 304 } 305 306 void PSInstance::SetVerbosity(Verbosity verbosity) { 307 verbosity_ = verbosity; 308 } 309 310 void PSInstance::VALog(Verbosity verbosity, const char *fmt, va_list args) { 311 if (verbosity <= verbosity_) { 312 fprintf(stderr, "ps: "); 313 vfprintf(stderr, fmt, args); 314 } 315 } 316 317 void PSInstance::Trace(const char *fmt, ...) { 318 va_list ap; 319 va_start(ap, fmt); 320 VALog(PSV_TRACE, fmt, ap); 321 va_end(ap); 322 } 323 324 void PSInstance::Log(const char *fmt, ...) { 325 va_list ap; 326 va_start(ap, fmt); 327 VALog(PSV_LOG, fmt, ap); 328 va_end(ap); 329 } 330 331 void PSInstance::Warn(const char *fmt, ...) { 332 va_list ap; 333 va_start(ap, fmt); 334 VALog(PSV_WARN, fmt, ap); 335 va_end(ap); 336 } 337 338 void PSInstance::Error(const char *fmt, ...) { 339 va_list ap; 340 va_start(ap, fmt); 341 VALog(PSV_ERROR, fmt, ap); 342 va_end(ap); 343 } 344 345 void PSInstance::SetEnabledEvents(uint32_t mask) { 346 events_enabled_ = mask; 347 if (mask == 0) { 348 static bool warn_once = true; 349 if (warn_once) { 350 Warn("PSInstance::SetEnabledEvents(mask) where mask == 0 will block\n"); 351 Warn("all events. This can come from PSEventSetFilter(PSE_NONE);\n"); 352 warn_once = false; 353 } 354 } 355 } 356 357 void PSInstance::PostEvent(PSEventType type) { 358 assert(PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST == type || 359 PSE_MOUSELOCK_MOUSELOCKLOST == type); 360 361 PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent)); 362 memset(env, 0, sizeof(*env)); 363 env->type = type; 364 event_queue_.Enqueue(env); 365 } 366 367 void PSInstance::PostEvent(PSEventType type, PP_Bool bool_value) { 368 assert(PSE_INSTANCE_DIDCHANGEFOCUS == type); 369 370 PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent)); 371 memset(env, 0, sizeof(*env)); 372 env->type = type; 373 env->as_bool = bool_value; 374 event_queue_.Enqueue(env); 375 } 376 377 void PSInstance::PostEvent(PSEventType type, PP_Resource resource) { 378 assert(PSE_INSTANCE_HANDLEINPUT == type || 379 PSE_INSTANCE_DIDCHANGEVIEW == type); 380 381 if (resource) { 382 PSInterfaceCore()->AddRefResource(resource); 383 } 384 PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent)); 385 memset(env, 0, sizeof(*env)); 386 env->type = type; 387 env->as_resource = resource; 388 event_queue_.Enqueue(env); 389 } 390 391 ssize_t PSInstance::TtyOutputHandler(const char* buf, size_t count) { 392 // We prepend the prefix_ to the data in buf, then package it up 393 // and post it as a message to javascript. 394 const char* data = static_cast<const char*>(buf); 395 std::string message = tty_prefix_; 396 message.append(data, count); 397 PostMessage(pp::Var(message)); 398 return count; 399 } 400 401 void PSInstance::MessageHandlerExit(const pp::Var& message) { 402 assert(message.is_string()); 403 pthread_mutex_lock(&exit_lock_); 404 pthread_cond_signal(&exit_cond_); 405 pthread_mutex_unlock(&exit_lock_); 406 } 407 408 void PSInstance::MessageHandlerInput(const pp::Var& key, 409 const pp::Var& message) { 410 std::string key_string = key.AsString(); 411 412 const char* filename = NULL; 413 if (key_string == tty_prefix_) { 414 filename = "/dev/tty"; 415 } else if (key_string == "jspipe1") { 416 filename = "/dev/jspipe1"; 417 } else if (key_string == "jspipe2") { 418 filename = "/dev/jspipe2"; 419 } else if (key_string == "jspipe3") { 420 filename = "/dev/jspipe3"; 421 } else { 422 Error("unexpected input key: %s", key_string.c_str()); 423 return; 424 } 425 426 int fd = open(filename, O_RDONLY); 427 if (fd < 0) { 428 Error("error opening file: %s (%s)", filename, strerror(errno)); 429 return; 430 } 431 432 int ret = ioctl(fd, NACL_IOC_HANDLEMESSAGE, &message.pp_var()); 433 if (ret != 0) { 434 Error("ioctl on %s failed: %d.\n", filename, ret); 435 close(fd); 436 return; 437 } 438 439 close(fd); 440 } 441 442 void PSInstance::HandleExitStatic(int status, void* user_data) { 443 PSInstance* instance = static_cast<PSInstance*>(user_data); 444 instance->ExitHandshake(status); 445 } 446 447 void PSInstance::HandleResize(int width, int height) { 448 struct winsize size; 449 memset(&size, 0, sizeof(size)); 450 size.ws_col = width; 451 size.ws_row = height; 452 ioctl(tty_fd_, TIOCSWINSZ, &size); 453 } 454 455 void PSInstance::MessageHandlerResize(const pp::Var& message) { 456 assert(message.is_array()); 457 pp::VarArray array(message); 458 assert(array.GetLength() == 2); 459 460 int width = array.Get(0).AsInt(); 461 int height = array.Get(1).AsInt(); 462 HandleResize(width, height); 463 } 464 465 ssize_t PSInstance::TtyOutputHandlerStatic(const char* buf, 466 size_t count, 467 void* user_data) { 468 PSInstance* instance = static_cast<PSInstance*>(user_data); 469 return instance->TtyOutputHandler(buf, count); 470 } 471 472 void PSInstance::MessageHandlerExitStatic(const pp::Var& key, 473 const pp::Var& value, 474 void* user_data) { 475 PSInstance* instance = static_cast<PSInstance*>(user_data); 476 instance->MessageHandlerExit(value); 477 } 478 479 void PSInstance::MessageHandlerInputStatic(const pp::Var& key, 480 const pp::Var& value, 481 void* user_data) { 482 PSInstance* instance = static_cast<PSInstance*>(user_data); 483 instance->MessageHandlerInput(key, value); 484 } 485 486 void PSInstance::MessageHandlerResizeStatic(const pp::Var& key, 487 const pp::Var& value, 488 void* user_data) { 489 PSInstance* instance = static_cast<PSInstance*>(user_data); 490 instance->MessageHandlerResize(value); 491 } 492 493 void PSInstance::RegisterMessageHandler(std::string message_name, 494 MessageHandler_t handler, 495 void* user_data) { 496 Trace("registering msg handler: %s", message_name.c_str()); 497 if (handler == NULL) { 498 message_handlers_.erase(message_name); 499 return; 500 } 501 502 MessageHandler message_handler = { handler, user_data }; 503 message_handlers_[message_name] = message_handler; 504 } 505 506 void PSInstance::PostEvent(PSEventType type, const PP_Var& var) { 507 assert(PSE_INSTANCE_HANDLEMESSAGE == type); 508 509 pp::Var event(var); 510 511 // If the message is a dictionary then see if it matches one 512 // of the specific handlers, then call that handler rather than 513 // queuing an event. 514 if (event.is_dictionary()) { 515 pp::VarDictionary dictionary(var); 516 pp::VarArray keys = dictionary.GetKeys(); 517 if (keys.GetLength() == 1) { 518 pp::Var key = keys.Get(0); 519 Trace("calling handler for: %s", key.AsString().c_str()); 520 MessageHandlerMap::iterator iter = message_handlers_.find(key.AsString()); 521 if (iter != message_handlers_.end()) { 522 MessageHandler_t handler = iter->second.handler; 523 void* user_data = iter->second.user_data; 524 handler(key, dictionary.Get(key), user_data); 525 return; 526 } 527 } 528 } 529 530 PSInterfaceVar()->AddRef(var); 531 PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent)); 532 memset(env, 0, sizeof(*env)); 533 env->type = type; 534 env->as_var = var; 535 event_queue_.Enqueue(env); 536 } 537 538 PSEvent* PSInstance::TryAcquireEvent() { 539 PSEvent* event; 540 while(true) { 541 event = event_queue_.Dequeue(false); 542 if (NULL == event) 543 break; 544 if (events_enabled_ & event->type) 545 break; 546 // Release filtered events & continue to acquire. 547 ReleaseEvent(event); 548 } 549 return event; 550 } 551 552 PSEvent* PSInstance::WaitAcquireEvent() { 553 PSEvent* event; 554 while(true) { 555 event = event_queue_.Dequeue(true); 556 if (events_enabled_ & event->type) 557 break; 558 // Release filtered events & continue to acquire. 559 ReleaseEvent(event); 560 } 561 return event; 562 } 563 564 void PSInstance::ReleaseEvent(PSEvent* event) { 565 if (event) { 566 switch(event->type) { 567 case PSE_INSTANCE_HANDLEMESSAGE: 568 PSInterfaceVar()->Release(event->as_var); 569 break; 570 case PSE_INSTANCE_HANDLEINPUT: 571 case PSE_INSTANCE_DIDCHANGEVIEW: 572 if (event->as_resource) { 573 PSInterfaceCore()->ReleaseResource(event->as_resource); 574 } 575 break; 576 default: 577 break; 578 } 579 free(event); 580 } 581 } 582 583 void PSInstance::HandleMessage(const pp::Var& message) { 584 Trace("Got Message\n"); 585 PostEvent(PSE_INSTANCE_HANDLEMESSAGE, message.pp_var()); 586 } 587 588 bool PSInstance::HandleInputEvent(const pp::InputEvent& event) { 589 PostEvent(PSE_INSTANCE_HANDLEINPUT, event.pp_resource()); 590 return true; 591 } 592 593 void PSInstance::DidChangeView(const pp::View& view) { 594 pp::Size new_size = view.GetRect().size(); 595 Log("Got View change: %d,%d\n", new_size.width(), new_size.height()); 596 PostEvent(PSE_INSTANCE_DIDCHANGEVIEW, view.pp_resource()); 597 } 598 599 void PSInstance::DidChangeFocus(bool focus) { 600 Log("Got Focus change: %s\n", focus ? "FOCUS ON" : "FOCUS OFF"); 601 PostEvent(PSE_INSTANCE_DIDCHANGEFOCUS, focus ? PP_TRUE : PP_FALSE); 602 } 603 604 void PSInstance::Graphics3DContextLost() { 605 Log("Graphics3DContextLost\n"); 606 PostEvent(PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST); 607 } 608 609 void PSInstance::MouseLockLost() { 610 Log("MouseLockLost\n"); 611 PostEvent(PSE_MOUSELOCK_MOUSELOCKLOST); 612 } 613