コード例 #1
0
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;
}
コード例 #2
0
apr_status_t LLPluginProcessParent::socketError(apr_status_t error)
{
    mSocketError = error;
    if (APR_STATUS_IS_EPIPE(error))
    {
        errorState();
    }
    return error;
};
コード例 #3
0
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;
}
コード例 #4
0
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;
}
コード例 #5
0
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;	
}
コード例 #6
0
// 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);
        }
    }
}
コード例 #7
0
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);
}
コード例 #8
0
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);
}
コード例 #9
0
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);
}