Esempio n. 1
0
 std::vector<std::basic_string<charT> > 
 split_unix(
       const std::basic_string<charT>& cmdline, 
       const std::basic_string<charT>& seperator, 
       const std::basic_string<charT>& quote, 
       const std::basic_string<charT>& escape)
 {   
    typedef boost::tokenizer< boost::escaped_list_separator<charT>, 
          typename std::basic_string<charT>::const_iterator, 
          std::basic_string<charT> >  tokenizerT;
       
    tokenizerT tok(cmdline.begin(), cmdline.end(), 
              boost::escaped_list_separator< charT >(escape, seperator, quote));
       
    std::vector< std::basic_string<charT> > result;
    for (typename tokenizerT::iterator cur_token(tok.begin()), end_token(tok.end()); cur_token != end_token; ++cur_token) {
       if (!cur_token->empty())
          result.push_back(*cur_token);
    }
    return result;
 }
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);
}