LLPluginProcessParent::~LLPluginProcessParent()
{
    LL_DEBUGS("Plugin") << "destructor" << LL_ENDL;

    // Remove from the global list before beginning destruction.
    {
        // Make sure to get the global mutex _first_ here, to avoid a possible deadlock against LLPluginProcessParent::poll()
        LLMutexLock lock(sInstancesMutex);
        {
            LLMutexLock lock2(&mIncomingQueueMutex);
            sInstances.remove(this);
        }
    }

    // Destroy any remaining shared memory regions
    sharedMemoryRegionsType::iterator iter;
    while((iter = mSharedMemoryRegions.begin()) != mSharedMemoryRegions.end())
    {
        // destroy the shared memory region
        iter->second->destroy();

        // and remove it from our map
        mSharedMemoryRegions.erase(iter);
    }

    mProcess.kill();
    killSockets();
}
LLPluginProcessParent::~LLPluginProcessParent()
{
	LL_DEBUGS("Plugin") << "destructor" << LL_ENDL;

	// Destroy any remaining shared memory regions
	sharedMemoryRegionsType::iterator iter;
	while((iter = mSharedMemoryRegions.begin()) != mSharedMemoryRegions.end())
	{
		// destroy the shared memory region
		iter->second->destroy();
		
		// and remove it from our map
		mSharedMemoryRegions.erase(iter);
	}
	
	// orphaning the process means it won't be killed when the LLProcessLauncher is destructed.
	// This is what we want -- it should exit cleanly once it notices the sockets have been closed.
	mProcess.orphan();
	killSockets();
}
void LLPluginProcessChild::idle(void)
{
	bool idle_again;
	do
	{
		if(APR_STATUS_IS_EOF(mSocketError))
		{
			// Plugin socket was closed.  This covers both normal plugin termination and host crashes.
			setState(STATE_ERROR);
		}
		else if(mSocketError != APR_SUCCESS)
		{
			LL_INFOS("Plugin") << "message pipe is in error state (" << mSocketError << "), moving to STATE_ERROR"<< LL_ENDL;
			setState(STATE_ERROR);
		}	

		if((mState > STATE_INITIALIZED) && (mMessagePipe == NULL))
		{
			// The pipe has been closed -- we're done.
			// TODO: This could be slightly more subtle, but I'm not sure it needs to be.
			LL_INFOS("Plugin") << "message pipe went away, moving to STATE_ERROR"<< LL_ENDL;
			setState(STATE_ERROR);
		}
	
		// 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;
		
		if(mInstance != NULL)
		{
			// Provide some time to the plugin
			mInstance->idle();
		}
		
		switch(mState)
		{
			case STATE_UNINITIALIZED:
			break;

			case STATE_INITIALIZED:
				if(mSocket->blockingConnect(mLauncherHost))
				{
					// This automatically sets mMessagePipe
					new LLPluginMessagePipe(this, mSocket);
					
					setState(STATE_CONNECTED);
				}
				else
				{
					// connect failed
					setState(STATE_ERROR);
				}
			break;
			
			case STATE_CONNECTED:
				sendMessageToParent(LLPluginMessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "hello"));
				setState(STATE_PLUGIN_LOADING);
			break;
						
			case STATE_PLUGIN_LOADING:
				if(!mPluginFile.empty())
				{
					mInstance = new LLPluginInstance(this);
					if(mInstance->load(mPluginDir, mPluginFile) == 0)
					{
						mHeartbeat.start();
						mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS);
						mCPUElapsed = 0.0f;
						setState(STATE_PLUGIN_LOADED);
					}
					else
					{
						setState(STATE_ERROR);
					}
				}
			break;
			
			case STATE_PLUGIN_LOADED:
				{
					setState(STATE_PLUGIN_INITIALIZING);
					LLPluginMessage message("base", "init");
					sendMessageToPlugin(message);
				}
			break;
			
			case STATE_PLUGIN_INITIALIZING:
				// waiting for init_response...
			break;
			
			case STATE_RUNNING:
				if(mInstance != NULL)
				{
					// Provide some time to the plugin
					LLPluginMessage message("base", "idle");
					message.setValueReal("time", PLUGIN_IDLE_SECONDS);
					sendMessageToPlugin(message);
					
					mInstance->idle();
					
					if(mHeartbeat.hasExpired())
					{
						
						// This just proves that we're not stuck down inside the plugin code.
						LLPluginMessage heartbeat(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "heartbeat");
						
						// Calculate the approximage CPU usage fraction (floating point value between 0 and 1) used by the plugin this heartbeat cycle.
						// Note that this will not take into account any threads or additional processes the plugin spawns, but it's a first approximation.
						// If we could write OS-specific functions to query the actual CPU usage of this process, that would be a better approximation.
						heartbeat.setValueReal("cpu_usage", mCPUElapsed / mHeartbeat.getElapsedTimeF64());
						
						sendMessageToParent(heartbeat);

						mHeartbeat.reset();
						mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS);
						mCPUElapsed = 0.0f;
					}
				}
				// receivePluginMessage will transition to STATE_UNLOADING
			break;

			case STATE_UNLOADING:
				if(mInstance != NULL)
				{
					sendMessageToPlugin(LLPluginMessage("base", "cleanup"));
					delete mInstance;
					mInstance = NULL;
				}
				setState(STATE_UNLOADED);
			break;
			
			case STATE_UNLOADED:
				killSockets();
				setState(STATE_DONE);
			break;

			case STATE_ERROR:
				// Close the socket to the launcher
				killSockets();				
				// TODO: Where do we go from here?  Just exit()?
				setState(STATE_DONE);
			break;
			
			case STATE_DONE:
				// just sit here.
				LL_WARNS("Plugin") << "Calling LLPluginProcessChild::idle while in STATE_DONE!" << LL_ENDL;
			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(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);
}