bool LLPluginProcessParent::accept() { bool result = false; apr_status_t status = APR_EGENERAL; mSocket = LLSocket::create(status, mListenSocket); if(status == APR_SUCCESS) { // llinfos << "SUCCESS" << llendl; // Success. Create a message pipe on the new socket new LLPluginMessagePipe(this, mSocket); result = true; } else { mSocket.reset(); // EAGAIN means "No incoming connections". This is not an error. if (!APR_STATUS_IS_EAGAIN(status)) { // Some other error. ll_apr_warn_status(status); errorState(); } } return result; }
apr_status_t LLPluginProcessParent::socketError(apr_status_t error) { mSocketError = error; if (APR_STATUS_IS_EPIPE(error)) { errorState(); } return error; };
int QState::qt_metacall(QMetaObject::Call _c, int _id, void **_a) { _id = QAbstractState::qt_metacall(_c, _id, _a); if (_id < 0) return _id; if (_c == QMetaObject::InvokeMetaMethod) { if (_id < 2) qt_static_metacall(this, _c, _id, _a); _id -= 2; } #ifndef QT_NO_PROPERTIES else if (_c == QMetaObject::ReadProperty) { void *_v = _a[0]; switch (_id) { case 0: *reinterpret_cast< QAbstractState**>(_v) = initialState(); break; case 1: *reinterpret_cast< QAbstractState**>(_v) = errorState(); break; case 2: *reinterpret_cast< ChildMode*>(_v) = childMode(); break; } _id -= 3; } else if (_c == QMetaObject::WriteProperty) { void *_v = _a[0]; switch (_id) { case 0: setInitialState(*reinterpret_cast< QAbstractState**>(_v)); break; case 1: setErrorState(*reinterpret_cast< QAbstractState**>(_v)); break; case 2: setChildMode(*reinterpret_cast< ChildMode*>(_v)); break; } _id -= 3; } else if (_c == QMetaObject::ResetProperty) { _id -= 3; } else if (_c == QMetaObject::QueryPropertyDesignable) { _id -= 3; } else if (_c == QMetaObject::QueryPropertyScriptable) { _id -= 3; } else if (_c == QMetaObject::QueryPropertyStored) { _id -= 3; } else if (_c == QMetaObject::QueryPropertyEditable) { _id -= 3; } else if (_c == QMetaObject::QueryPropertyUser) { _id -= 3; } #endif // QT_NO_PROPERTIES return _id; }
int QtStateMachine::qt_metacall(QMetaObject::Call _c, int _id, void **_a) { _id = QObject::qt_metacall(_c, _id, _a); if (_id < 0) return _id; if (_c == QMetaObject::InvokeMetaMethod) { switch (_id) { case 0: started(); break; case 1: stopped(); break; case 2: finished(); break; case 3: start(); break; case 4: stop(); break; case 5: d_func()->_q_start(); break; case 6: d_func()->_q_process(); break; default: ; } _id -= 7; } #ifndef QT_NO_PROPERTIES else if (_c == QMetaObject::ReadProperty) { void *_v = _a[0]; switch (_id) { case 0: *reinterpret_cast< QtState**>(_v) = rootState(); break; case 1: *reinterpret_cast< QtAbstractState**>(_v) = initialState(); break; case 2: *reinterpret_cast< QtAbstractState**>(_v) = errorState(); break; case 3: *reinterpret_cast< QString*>(_v) = errorString(); break; } _id -= 4; } else if (_c == QMetaObject::WriteProperty) { void *_v = _a[0]; switch (_id) { case 1: setInitialState(*reinterpret_cast< QtAbstractState**>(_v)); break; case 2: setErrorState(*reinterpret_cast< QtAbstractState**>(_v)); break; } _id -= 4; } else if (_c == QMetaObject::ResetProperty) { _id -= 4; } else if (_c == QMetaObject::QueryPropertyDesignable) { _id -= 4; } else if (_c == QMetaObject::QueryPropertyScriptable) { _id -= 4; } else if (_c == QMetaObject::QueryPropertyStored) { _id -= 4; } else if (_c == QMetaObject::QueryPropertyEditable) { _id -= 4; } else if (_c == QMetaObject::QueryPropertyUser) { _id -= 4; } #endif // QT_NO_PROPERTIES return _id; }
bool LLPluginProcessParent::accept() { bool result = false; apr_status_t status = APR_EGENERAL; apr_socket_t *new_socket = NULL; status = apr_socket_accept( &new_socket, mListenSocket->getSocket(), gAPRPoolp); if(status == APR_SUCCESS) { // llinfos << "SUCCESS" << llendl; // Success. Create a message pipe on the new socket // we MUST create a new pool for the LLSocket, since it will take ownership of it and delete it in its destructor! apr_pool_t* new_pool = NULL; status = apr_pool_create(&new_pool, gAPRPoolp); mSocket = LLSocket::create(new_socket, new_pool); new LLPluginMessagePipe(this, mSocket); result = true; } else if(APR_STATUS_IS_EAGAIN(status)) { // llinfos << "EAGAIN" << llendl; // No incoming connections. This is not an error. status = APR_SUCCESS; } else { // llinfos << "Error:" << llendl; ll_apr_warn_status(status); // Some other error. errorState(); } return result; }
// This is the viewer process (the parent process). // // This function is called for messages that have to // be written to the plugin. // Note that LLPLUGIN_MESSAGE_CLASS_INTERNAL messages // are not sent to the plugin, but are handled here. void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) { std::string message_class = message.getClass(); if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL) { // internal messages should be handled here std::string message_name = message.getName(); if(message_name == "hello") { if(mState == STATE_CONNECTED) { // Plugin host has launched. Tell it which plugin to load. setState(STATE_HELLO); } else { LL_WARNS("Plugin") << "received hello message in wrong state -- bailing out" << LL_ENDL; errorState(); } } else if(message_name == "load_plugin_response") { if(mState == STATE_LOADING) { // Plugin has been loaded. mPluginVersionString = message.getValue("plugin_version"); LL_INFOS("Plugin") << "plugin version string: " << mPluginVersionString << LL_ENDL; // Check which message classes/versions the plugin supports. // TODO: check against current versions // TODO: kill plugin on major mismatches? mMessageClassVersions = message.getValueLLSD("versions"); LLSD::map_iterator iter; for(iter = mMessageClassVersions.beginMap(); iter != mMessageClassVersions.endMap(); iter++) { LL_INFOS("Plugin") << "message class: " << iter->first << " -> version: " << iter->second.asString() << LL_ENDL; } // Send initial sleep time setSleepTime(mSleepTime, true); setState(STATE_RUNNING); } else { LL_WARNS("Plugin") << "received load_plugin_response message in wrong state -- bailing out" << LL_ENDL; errorState(); } } else if(message_name == "heartbeat") { // this resets our timer. mHeartbeat.setTimerExpirySec(mPluginLockupTimeout); mCPUUsage = message.getValueReal("cpu_usage"); LL_DEBUGS("PluginHeartbeat") << "cpu usage reported as " << mCPUUsage << LL_ENDL; } else if(message_name == "shutdown") { LL_INFOS("Plugin") << "received shutdown message" << LL_ENDL; mReceivedShutdown = true; mOwner->receivedShutdown(); } else if(message_name == "shm_add_response") { // Nothing to do here. } else if(message_name == "shm_remove_response") { std::string name = message.getValue("name"); sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); if(iter != mSharedMemoryRegions.end()) { // destroy the shared memory region iter->second->destroy(); // and remove it from our map mSharedMemoryRegions.erase(iter); } } else if(message_name == "log_message") { std::string msg=message.getValue("message"); S32 level=message.getValueS32("log_level"); switch(level) { case LLPluginMessage::LOG_LEVEL_DEBUG: LL_DEBUGS("Plugin child")<<msg<<LL_ENDL; break; case LLPluginMessage::LOG_LEVEL_INFO: LL_INFOS("Plugin child")<<msg<<LL_ENDL; break; case LLPluginMessage::LOG_LEVEL_WARN: LL_WARNS("Plugin child")<<msg<<LL_ENDL; break; case LLPluginMessage::LOG_LEVEL_ERR: LL_ERRS("Plugin child")<<msg<<LL_ENDL; break; default: break; } } else { LL_WARNS("Plugin") << "Unknown internal message from child: " << message_name << LL_ENDL; } } else { if(mOwner != NULL) { mOwner->receivePluginMessage(message); } } }
void LLPluginProcessParent::idle(void) { bool idle_again; do { // process queued messages mIncomingQueueMutex.lock(); while(!mIncomingQueue.empty()) { LLPluginMessage message = mIncomingQueue.front(); mIncomingQueue.pop(); mIncomingQueueMutex.unlock(); receiveMessage(message); mIncomingQueueMutex.lock(); } mIncomingQueueMutex.unlock(); // Give time to network processing if(mMessagePipe) { // Drain any queued outgoing messages mMessagePipe->pumpOutput(); // Only do input processing here if this instance isn't in a pollset. if(!mPolledInput) { mMessagePipe->pumpInput(); } } if(mState <= STATE_RUNNING) { if(APR_STATUS_IS_EOF(mSocketError)) { // Plugin socket was closed. This covers both normal plugin termination and plugin crashes. errorState(); } else if(mSocketError != APR_SUCCESS) { // The socket is in an error state -- the plugin is gone. LL_WARNS("Plugin") << "Socket hit an error state (" << mSocketError << ")" << LL_ENDL; errorState(); } } // If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState(). // USE THIS CAREFULLY, since it can starve other code. Specifically make sure there's no way to get into a closed cycle and never return. // When in doubt, don't do it. idle_again = false; switch(mState) { case STATE_UNINITIALIZED: break; case STATE_INITIALIZED: { apr_status_t status = APR_SUCCESS; apr_sockaddr_t* addr = NULL; mListenSocket = LLSocket::create(LLSocket::STREAM_TCP); mBoundPort = 0; // This code is based on parts of LLSocket::create() in lliosocket.cpp. status = apr_sockaddr_info_get( &addr, "127.0.0.1", APR_INET, 0, // port 0 = ephemeral ("find me a port") 0, AIAPRRootPool::get()()); if(ll_apr_warn_status(status)) { killSockets(); errorState(); break; } // This allows us to reuse the address on quick down/up. This is unlikely to create problems. ll_apr_warn_status(apr_socket_opt_set(mListenSocket->getSocket(), APR_SO_REUSEADDR, 1)); status = apr_socket_bind(mListenSocket->getSocket(), addr); if(ll_apr_warn_status(status)) { killSockets(); errorState(); break; } // Get the actual port the socket was bound to { apr_sockaddr_t* bound_addr = NULL; if(ll_apr_warn_status(apr_socket_addr_get(&bound_addr, APR_LOCAL, mListenSocket->getSocket()))) { killSockets(); errorState(); break; } mBoundPort = bound_addr->port; if(mBoundPort == 0) { LL_WARNS("Plugin") << "Bound port number unknown, bailing out." << LL_ENDL; killSockets(); errorState(); break; } } LL_DEBUGS("Plugin") << "Bound tcp socket to port: " << addr->port << LL_ENDL; // Make the listen socket non-blocking status = apr_socket_opt_set(mListenSocket->getSocket(), APR_SO_NONBLOCK, 1); if(ll_apr_warn_status(status)) { killSockets(); errorState(); break; } apr_socket_timeout_set(mListenSocket->getSocket(), 0); if(ll_apr_warn_status(status)) { killSockets(); errorState(); break; } // If it's a stream based socket, we need to tell the OS // to keep a queue of incoming connections for ACCEPT. status = apr_socket_listen( mListenSocket->getSocket(), 10); // FIXME: Magic number for queue size if(ll_apr_warn_status(status)) { killSockets(); errorState(); break; } // If we got here, we're listening. setState(STATE_LISTENING); } break; case STATE_LISTENING: { // Launch the plugin process. // Only argument to the launcher is the port number we're listening on std::stringstream stream; stream << mBoundPort; mProcess.addArgument(stream.str()); if(mProcess.launch() != 0) { errorState(); } else { if(mDebug) { // If we're set to debug, start up a gdb instance in a new terminal window and have it attach to the plugin process and continue. std::stringstream cmd; #if LL_DARWIN // The command we're constructing would look like this on the command line: // osascript -e 'tell application "Terminal"' -e 'set win to do script "gdb -pid 12345"' -e 'do script "continue" in win' -e 'end tell' mDebugger.setExecutable("/usr/bin/osascript"); mDebugger.addArgument("-e"); mDebugger.addArgument("tell application \"Terminal\""); mDebugger.addArgument("-e"); cmd << "set win to do script \"gdb -pid " << mProcess.getProcessID() << "\""; mDebugger.addArgument(cmd.str()); mDebugger.addArgument("-e"); mDebugger.addArgument("do script \"continue\" in win"); mDebugger.addArgument("-e"); mDebugger.addArgument("end tell"); mDebugger.launch(); #elif LL_LINUX // The command we're constructing would look like this on the command line: // /usr/bin/xterm -geometry 160x24-0+0 -e '/usr/bin/gdb -n /proc/12345/exe 12345' // This can be changed by setting the following environment variables, for example: // export LL_DEBUG_TERMINAL_COMMAND="/usr/bin/gnome-terminal --geometry=165x24-0+0 -e %s" // export LL_DEBUG_GDB_PATH=/usr/bin/gdb char const* env; std::string const terminal_command = (env = getenv("LL_DEBUG_TERMINAL_COMMAND")) ? env : "/usr/bin/xterm -geometry 160x24+0+0 -e %s"; char const* const gdb_path = (env = getenv("LL_DEBUG_GDB_PATH")) ? env : "/usr/bin/gdb"; cmd << gdb_path << " -n /proc/" << mProcess.getProcessID() << "/exe " << mProcess.getProcessID(); typedef boost::tokenizer< boost::escaped_list_separator< char>, typename std::basic_string< char>::const_iterator, std::basic_string<char> > tokenizerT; tokenizerT tok(terminal_command.begin(), terminal_command.end(), boost::escaped_list_separator< char >("\\", " ", "'\"")); std::vector< std::basic_string<char> > tokens; for (typename tokenizerT::iterator cur_token(tok.begin()), end_token(tok.end()); cur_token != end_token; ++cur_token) { if (!cur_token->empty()) tokens.push_back(*cur_token); } std::vector<std::string>::iterator token = tokens.begin(); mDebugger.setExecutable(*token); while (++token != tokens.end()) { if (*token == "%s") { mDebugger.addArgument(cmd.str()); } else { mDebugger.addArgument(*token); } } mDebugger.launch(); #endif } // This will allow us to time out if the process never starts. mHeartbeat.start(); mHeartbeat.setTimerExpirySec(mPluginLaunchTimeout); setState(STATE_LAUNCHED); } } break; case STATE_LAUNCHED: // waiting for the plugin to connect if(pluginLockedUpOrQuit()) { errorState(); } else { // Check for the incoming connection. if(accept()) { // Stop listening on the server port mListenSocket.reset(); setState(STATE_CONNECTED); } } break; case STATE_CONNECTED: // waiting for hello message from the plugin if(pluginLockedUpOrQuit()) { errorState(); } break; case STATE_HELLO: LL_DEBUGS("Plugin") << "received hello message" << LL_ENDL; // Send the message to load the plugin { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "load_plugin"); message.setValue("file", mPluginFile); sendMessage(message); } setState(STATE_LOADING); break; case STATE_LOADING: // The load_plugin_response message will kick us from here into STATE_RUNNING if(pluginLockedUpOrQuit()) { errorState(); } break; case STATE_RUNNING: if(pluginLockedUpOrQuit()) { errorState(); } break; case STATE_EXITING: if(!mProcess.isRunning()) { setState(STATE_CLEANUP); } else if(pluginLockedUp()) { LL_WARNS("Plugin") << "timeout in exiting state, bailing out" << LL_ENDL; errorState(); } break; case STATE_LAUNCH_FAILURE: if(mOwner != NULL) { mOwner->pluginLaunchFailed(); } setState(STATE_CLEANUP); break; case STATE_ERROR: if(mOwner != NULL) { mOwner->pluginDied(); } setState(STATE_CLEANUP); break; case STATE_CLEANUP: mProcess.kill(); killSockets(); setState(STATE_DONE); break; case STATE_DONE: // just sit here. break; } } while (idle_again); }
void LLPluginProcessParent::idle(void) { bool idle_again; do { // Give time to network processing if(mMessagePipe) { if(!mMessagePipe->pump()) { // LL_WARNS("Plugin") << "Message pipe hit an error state" << LL_ENDL; errorState(); } } if((mSocketError != APR_SUCCESS) && (mState <= STATE_RUNNING)) { // The socket is in an error state -- the plugin is gone. LL_WARNS("Plugin") << "Socket hit an error state (" << mSocketError << ")" << LL_ENDL; errorState(); } // If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState(). // USE THIS CAREFULLY, since it can starve other code. Specifically make sure there's no way to get into a closed cycle and never return. // When in doubt, don't do it. idle_again = false; switch(mState) { case STATE_UNINITIALIZED: break; case STATE_INITIALIZED: { apr_status_t status = APR_SUCCESS; apr_sockaddr_t* addr = NULL; mListenSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP); mBoundPort = 0; // This code is based on parts of LLSocket::create() in lliosocket.cpp. status = apr_sockaddr_info_get( &addr, "127.0.0.1", APR_INET, 0, // port 0 = ephemeral ("find me a port") 0, gAPRPoolp); if(ll_apr_warn_status(status)) { killSockets(); errorState(); break; } // This allows us to reuse the address on quick down/up. This is unlikely to create problems. ll_apr_warn_status(apr_socket_opt_set(mListenSocket->getSocket(), APR_SO_REUSEADDR, 1)); status = apr_socket_bind(mListenSocket->getSocket(), addr); if(ll_apr_warn_status(status)) { killSockets(); errorState(); break; } // Get the actual port the socket was bound to { apr_sockaddr_t* bound_addr = NULL; if(ll_apr_warn_status(apr_socket_addr_get(&bound_addr, APR_LOCAL, mListenSocket->getSocket()))) { killSockets(); errorState(); break; } mBoundPort = bound_addr->port; if(mBoundPort == 0) { LL_WARNS("Plugin") << "Bound port number unknown, bailing out." << LL_ENDL; killSockets(); errorState(); break; } } LL_DEBUGS("Plugin") << "Bound tcp socket to port: " << addr->port << LL_ENDL; // Make the listen socket non-blocking status = apr_socket_opt_set(mListenSocket->getSocket(), APR_SO_NONBLOCK, 1); if(ll_apr_warn_status(status)) { killSockets(); errorState(); break; } apr_socket_timeout_set(mListenSocket->getSocket(), 0); if(ll_apr_warn_status(status)) { killSockets(); errorState(); break; } // If it's a stream based socket, we need to tell the OS // to keep a queue of incoming connections for ACCEPT. status = apr_socket_listen( mListenSocket->getSocket(), 10); // FIXME: Magic number for queue size if(ll_apr_warn_status(status)) { killSockets(); errorState(); break; } // If we got here, we're listening. setState(STATE_LISTENING); } break; case STATE_LISTENING: { // Launch the plugin process. // Only argument to the launcher is the port number we're listening on std::stringstream stream; stream << mBoundPort; mProcess.addArgument(stream.str()); if(mProcess.launch() != 0) { errorState(); } else { if(mDebug) { #if LL_DARWIN // If we're set to debug, start up a gdb instance in a new terminal window and have it attach to the plugin process and continue. // The command we're constructing would look like this on the command line: // osascript -e 'tell application "Terminal"' -e 'set win to do script "gdb -pid 12345"' -e 'do script "continue" in win' -e 'end tell' std::stringstream cmd; mDebugger.setExecutable("/usr/bin/osascript"); mDebugger.addArgument("-e"); mDebugger.addArgument("tell application \"Terminal\""); mDebugger.addArgument("-e"); cmd << "set win to do script \"gdb -pid " << mProcess.getProcessID() << "\""; mDebugger.addArgument(cmd.str()); mDebugger.addArgument("-e"); mDebugger.addArgument("do script \"continue\" in win"); mDebugger.addArgument("-e"); mDebugger.addArgument("end tell"); mDebugger.launch(); #endif } // This will allow us to time out if the process never starts. mHeartbeat.start(); mHeartbeat.setTimerExpirySec(mPluginLaunchTimeout); setState(STATE_LAUNCHED); } } break; case STATE_LAUNCHED: // waiting for the plugin to connect if(pluginLockedUpOrQuit()) { errorState(); } else { // Check for the incoming connection. if(accept()) { // Stop listening on the server port mListenSocket.reset(); setState(STATE_CONNECTED); } } break; case STATE_CONNECTED: // waiting for hello message from the plugin if(pluginLockedUpOrQuit()) { errorState(); } break; case STATE_HELLO: LL_DEBUGS("Plugin") << "received hello message" << llendl; // Send the message to load the plugin { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "load_plugin"); message.setValue("file", mPluginFile); message.setValue("user_data_path", mUserDataPath); sendMessage(message); } setState(STATE_LOADING); break; case STATE_LOADING: // The load_plugin_response message will kick us from here into STATE_RUNNING if(pluginLockedUpOrQuit()) { errorState(); } break; case STATE_RUNNING: if(pluginLockedUpOrQuit()) { errorState(); } break; case STATE_EXITING: if(!mProcess.isRunning()) { setState(STATE_CLEANUP); } else if(pluginLockedUp()) { LL_WARNS("Plugin") << "timeout in exiting state, bailing out" << llendl; errorState(); } break; case STATE_LAUNCH_FAILURE: if(mOwner != NULL) { mOwner->pluginLaunchFailed(); } setState(STATE_CLEANUP); break; case STATE_ERROR: if(mOwner != NULL) { mOwner->pluginDied(); } setState(STATE_CLEANUP); break; case STATE_CLEANUP: // Don't do a kill here anymore -- closing the sockets is the new 'kill'. mProcess.orphan(); killSockets(); setState(STATE_DONE); break; case STATE_DONE: // just sit here. break; } } while (idle_again); }
void LLPluginProcessParent::idle(void) { bool idle_again; do { // process queued messages mIncomingQueueMutex.lock(); while(!mIncomingQueue.empty()) { LLPluginMessage message = mIncomingQueue.front(); mIncomingQueue.pop(); mIncomingQueueMutex.unlock(); receiveMessage(message); mIncomingQueueMutex.lock(); } mIncomingQueueMutex.unlock(); // Give time to network processing if(mMessagePipe) { // Drain any queued outgoing messages mMessagePipe->pumpOutput(); // Only do input processing here if this instance isn't in a pollset. if(!mPolledInput) { mMessagePipe->pumpInput(); } } if(mState <= STATE_RUNNING) { if(APR_STATUS_IS_EOF(mSocketError)) { // Plugin socket was closed. This covers both normal plugin termination and plugin crashes. errorState(); } else if(mSocketError != APR_SUCCESS) { // The socket is in an error state -- the plugin is gone. LL_WARNS("Plugin") << "Socket hit an error state (" << mSocketError << ")" << LL_ENDL; errorState(); } } // If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState(). // USE THIS CAREFULLY, since it can starve other code. Specifically make sure there's no way to get into a closed cycle and never return. // When in doubt, don't do it. idle_again = false; switch(mState) { case STATE_UNINITIALIZED: break; case STATE_INITIALIZED: { apr_status_t status = APR_SUCCESS; apr_sockaddr_t* addr = NULL; mListenSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP); mBoundPort = 0; // This code is based on parts of LLSocket::create() in lliosocket.cpp. status = apr_sockaddr_info_get( &addr, "127.0.0.1", APR_INET, mPortToBind, // port 0 = ephemeral ("find me a port") 0, gAPRPoolp); if(ll_apr_warn_status(status)) { killSockets(); errorState(); break; } // This allows us to reuse the address on quick down/up. This is unlikely to create problems. ll_apr_warn_status(apr_socket_opt_set(mListenSocket->getSocket(), APR_SO_REUSEADDR, 1)); status = apr_socket_bind(mListenSocket->getSocket(), addr); if(ll_apr_warn_status(status)) { killSockets(); errorState(); break; } // Get the actual port the socket was bound to { apr_sockaddr_t* bound_addr = NULL; if(ll_apr_warn_status(apr_socket_addr_get(&bound_addr, APR_LOCAL, mListenSocket->getSocket()))) { killSockets(); errorState(); break; } mBoundPort = bound_addr->port; if(mBoundPort == 0) { LL_WARNS("Plugin") << "Bound port number unknown, bailing out." << LL_ENDL; killSockets(); // <ND> FIRE-3877; Some drivers, eg bigfoot. Refuse to tell us which port is used when the socket is bound on port 0 (= choose a free port). // If not out of retry attempts, choose a random port between 5500 - 60000 and try again. if( mBindRetries > 10 ) //In theory we could have bad luck and randomly draft already used ports each try. In practice we already deal with a buggy driver anyway. So just fail instead hogging resources in a loop. errorState(); else { ++mBindRetries; mPortToBind = ll_rand(55000)+5000; // Ports < 4096 are reserved for root (at least on BSD like systems), do never touch them. setState( STATE_INITIALIZED ); idle_again = true; // Just try a new loop to bind the socket } // </ND> break; } } LL_DEBUGS("Plugin") << "Bound tcp socket to port: " << addr->port << LL_ENDL; // Make the listen socket non-blocking status = apr_socket_opt_set(mListenSocket->getSocket(), APR_SO_NONBLOCK, 1); if(ll_apr_warn_status(status)) { killSockets(); errorState(); break; } apr_socket_timeout_set(mListenSocket->getSocket(), 0); if(ll_apr_warn_status(status)) { killSockets(); errorState(); break; } // If it's a stream based socket, we need to tell the OS // to keep a queue of incoming connections for ACCEPT. status = apr_socket_listen( mListenSocket->getSocket(), 10); // FIXME: Magic number for queue size if(ll_apr_warn_status(status)) { killSockets(); errorState(); break; } // If we got here, we're listening. setState(STATE_LISTENING); } break; case STATE_LISTENING: { // Launch the plugin process. // Only argument to the launcher is the port number we're listening on mProcessParams.args.add(stringize(mBoundPort)); if (! (mProcess = LLProcess::create(mProcessParams))) { errorState(); } else { if(mDebug) { #if LL_DARWIN // If we're set to debug, start up a gdb instance in a new terminal window and have it attach to the plugin process and continue. // The command we're constructing would look like this on the command line: // osascript -e 'tell application "Terminal"' -e 'set win to do script "gdb -pid 12345"' -e 'do script "continue" in win' -e 'end tell' LLProcess::Params params; params.executable = "/usr/bin/osascript"; params.args.add("-e"); params.args.add("tell application \"Terminal\""); params.args.add("-e"); params.args.add(STRINGIZE("set win to do script \"gdb -pid " << mProcess->getProcessID() << "\"")); params.args.add("-e"); params.args.add("do script \"continue\" in win"); params.args.add("-e"); params.args.add("end tell"); mDebugger = LLProcess::create(params); #endif } // This will allow us to time out if the process never starts. mHeartbeat.start(); mHeartbeat.setTimerExpirySec(mPluginLaunchTimeout); setState(STATE_LAUNCHED); } } break; case STATE_LAUNCHED: // waiting for the plugin to connect if(pluginLockedUpOrQuit()) { errorState(); } else { // Check for the incoming connection. if(accept()) { // Stop listening on the server port mListenSocket.reset(); setState(STATE_CONNECTED); } } break; case STATE_CONNECTED: // waiting for hello message from the plugin if(pluginLockedUpOrQuit()) { errorState(); } break; case STATE_HELLO: LL_DEBUGS("Plugin") << "received hello message" << LL_ENDL; // Send the message to load the plugin { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "load_plugin"); message.setValue("file", mPluginFile); message.setValue("dir", mPluginDir); sendMessage(message); } setState(STATE_LOADING); break; case STATE_LOADING: // The load_plugin_response message will kick us from here into STATE_RUNNING if(pluginLockedUpOrQuit()) { errorState(); } break; case STATE_RUNNING: if(pluginLockedUpOrQuit()) { errorState(); } break; case STATE_EXITING: if (! LLProcess::isRunning(mProcess)) { setState(STATE_CLEANUP); } else if(pluginLockedUp()) { LL_WARNS("Plugin") << "timeout in exiting state, bailing out" << LL_ENDL; errorState(); } break; case STATE_LAUNCH_FAILURE: if(mOwner != NULL) { mOwner->pluginLaunchFailed(); } setState(STATE_CLEANUP); break; case STATE_ERROR: if(mOwner != NULL) { mOwner->pluginDied(); } setState(STATE_CLEANUP); break; case STATE_CLEANUP: LLProcess::kill(mProcess); killSockets(); setState(STATE_DONE); break; case STATE_DONE: // just sit here. break; } } while (idle_again); }