<commit_before>/*
Copyright (c) 2013, David C Horton

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <iostream>

#include <boost/bind.hpp>
#include <boost/tokenizer.hpp>
#include <boost/foreach.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/asio.hpp>

#include "sofia-msg.hpp"
#include "client-controller.hpp"
#include "controller.hpp"

namespace drachtio {

    ClientController::ClientController( DrachtioController* pController, string& address, unsigned int port ) :
        m_pController( pController ),
        m_endpoint(  boost::asio::ip::tcp::v4(), port ),
        m_acceptor( m_ioservice, m_endpoint ) {

        boost::thread t(&ClientController::threadFunc, this) ;
        m_thread.swap( t ) ;

        this->start_accept() ;
    }
    ClientController::~ClientController() {
        this->stop() ;
   }
    void ClientController::onTimer( const boost::system::error_code& e, boost::asio::deadline_timer* t ) {
        //m_pController->processWatchdogTimer() ;
        //t->expires_at(t->expires_at() + boost::posix_time::seconds(25));
        //t->async_wait(boost::bind(&ClientController::onTimer, this, boost::asio::placeholders::error, t ));
    }

    void ClientController::threadFunc() {

        DR_LOG(log_debug) << "Client controller thread id: " << boost::this_thread::get_id() << endl ;

        boost::asio::deadline_timer t(m_ioservice, boost::posix_time::seconds(25) ) ;
        t.async_wait(boost::bind(&ClientController::onTimer, this, boost::asio::placeholders::error, &t ));

        /* to make sure the event loop doesn't terminate when there is no work to do */
        boost::asio::io_service::work work(m_ioservice);

        for(;;) {

            try {
                DR_LOG(log_notice) << "ClientController: io_service run loop started" << endl ;
                m_ioservice.run() ;
                DR_LOG(log_notice) << "ClientController: io_service run loop ended normally" << endl ;
                break ;
            }
            catch( std::exception& e) {
                DR_LOG(log_error) << "Error in event thread: " << string( e.what() ) << endl ;
                break ;
            }
        }
    }
    void ClientController::join( client_ptr client ) {
        m_clients.insert( client ) ;
        client_weak_ptr p( client ) ;
        for( vector<string>::const_iterator it = client->getServices().begin(); it != client->getServices().end(); ++it ) {
           m_services.insert(map_of_services::value_type(*it, p)) ;
        }
       DR_LOG(log_debug) << "Added client, count of connected clients is now: " << m_clients.size() << endl ;
    }
    void ClientController::leave( client_ptr client ) {
        m_clients.erase( client ) ;
        DR_LOG(log_debug) << "Removed client, count of connected clients is now: " << m_clients.size() << endl ;
    }

	void ClientController::start_accept() {
		client_ptr new_session( new Client( m_ioservice, *this ) ) ;
		m_acceptor.async_accept( new_session->socket(), boost::bind(&ClientController::accept_handler, this, new_session, boost::asio::placeholders::error));
    }
	void ClientController::accept_handler( client_ptr session, const boost::system::error_code& ec) {
        if(!ec) {
            session->start() ;
        }
        start_accept();
    }
    bool ClientController::wants_requests( client_ptr client, const string& verb ) {
        RequestSpecifier spec( client ) ;
        boost::lock_guard<boost::mutex> l( m_lock ) ;
        m_request_types.insert( map_of_request_types::value_type(verb, spec)) ;
        DR_LOG(log_debug) << "Added client for " << verb << " requests" << endl ;
        //TODO: validate the verb is supported
        return true ;
    }

    bool ClientController::route_request_outside_dialog( nta_incoming_t* irq, sip_t const *sip, const string& transactionId ) {

        boost::shared_ptr<SofiaMsg> sm = boost::make_shared<SofiaMsg>( irq, sip ) ;

        string method_name = sip->sip_request->rq_method_name ;
        transform(method_name.begin(), method_name.end(), method_name.begin(), ::tolower);

        boost::lock_guard<boost::mutex> l( m_lock ) ;
        client_ptr client ;
        string matchId ;
        pair<map_of_request_types::iterator,map_of_request_types::iterator> pair = m_request_types.equal_range( method_name ) ;
        for( map_of_request_types::iterator it = pair.first; it != pair.second; ) {
            RequestSpecifier& spec = it->second ;

            if( !spec.client() ) {
                /* note: our weak pointer may be to a client that has disconnected, so we need to check and remove them from the map
                    at this point if that is so.
                */
                DR_LOG(log_debug) << "Removing disconnected client while iterating" << endl ;
                m_request_types.erase(it++) ;
                continue ;
            }
            client = spec.client() ;
            break ;
        }

        if( !client ) {
            DR_LOG(log_info) << "No clients found to handle incoming " << method_name << " request" << endl ;
           return false ;
        }

        /* we've selected a client for this message */
        string json = sm->str() ;

        JsonMsg jmsg( json ) ;

        m_mapTransactions.insert( mapTransactions::value_type(transactionId,client)) ;

        m_ioservice.post( boost::bind(&Client::sendRequestOutsideDialog, client, transactionId, json) ) ;

        return true ;
    }
    void ClientController::respondToSipRequest( const string& transactionId, boost::shared_ptr<JsonMsg> pMsg ) {
         m_pController->getDialogController()->respondToSipRequest( transactionId, pMsg ) ;
    }

    bool ClientController::route_request_inside_dialog( nta_incoming_t* irq, sip_t const *sip, const string& dialogId ) {
        client_ptr client = this->findClientForDialog( dialogId );
        if( !client ) {
            DR_LOG(log_warning) << "ClientController::route_request_inside_dialog - client managing dialog has disconnected: " << dialogId << endl ;
            return false ;
        }

        boost::shared_ptr<SofiaMsg> sm = boost::make_shared<SofiaMsg>( irq, sip ) ;
        string json = sm->str() ;
        JsonMsg jmsg( json ) ;

        m_ioservice.post( boost::bind(&Client::sendRequestWithinDialog, client, dialogId, json) ) ;

        return true ;
    }
    bool ClientController::route_response_inside_transaction( nta_outgoing_t* orq, sip_t const *sip, const string& transactionId ) {
        client_ptr client = this->findClientForTransaction( transactionId );
        if( !client ) {
            DR_LOG(log_warning) << "ClientController::route_response_inside_transaction - client managing transaction has disconnected: " << transactionId << endl ;
            return false ;
        }

        boost::shared_ptr<SofiaMsg> sm = boost::make_shared<SofiaMsg>( orq, sip ) ;
        string json = sm->str() ;
        JsonMsg jmsg( json ) ;

        m_ioservice.post( boost::bind(&Client::sendResponseWithinTransaction, client, transactionId, json) ) ;

        return true ;
    }
    void ClientController::addDialogForTransaction( const string& transactionId, const string& dialogId ) {
        mapTransactions::iterator it = m_mapTransactions.find( transactionId ) ;
        if( m_mapTransactions.end() != it ) {
            m_mapDialogs.insert( mapDialogs::value_type(dialogId, it->second ) ) ;
        }
        else {
            DR_LOG(log_error) << "ClientController::addDialogForTransaction - transaction id " << transactionId << " not found" << endl ;
            assert(false) ;
        }
        DR_LOG(log_debug) << "ClientController::addDialogForTransaction - transaction id " << transactionId <<
            " has associated dialog " << dialogId << endl ;

        client_ptr client = this->findClientForDialog( dialogId );
        if( !client ) {
            m_mapDialogs.erase( dialogId ) ;
            m_mapTransactions.erase( transactionId ) ;
            DR_LOG(log_warning) << "ClientController::addDialogForTransaction - client managing dialog has disconnected: " << dialogId << endl ;
            return  ;
        }

        m_ioservice.post( boost::bind(&Client::sendDialogInfo, client, dialogId, transactionId) ) ;

    }
    bool ClientController::sendSipRequest( client_ptr client, boost::shared_ptr<JsonMsg> pMsg, const string& rid ) {
        ostringstream o ;
        m_mapRequests.insert( mapRequests::value_type( rid, client)) ;
        string strDialogId ;
        if( pMsg->get<string>("data.dialogId", strDialogId) ) {
            if( m_pController->sendRequestInsideDialog( pMsg, rid ) < 0 ) {
                o << "{\"success\": false, \"reason\": \"unknown sip dialog\"}" ;
                client->sendResponse( rid, o.str() ) ;
                return false ;
            }
            return true ;
        }
        else {
            string method ;
            string transactionId ;
            pMsg->get<string>("data.method", method ) ;
            if( 0 == method.compare("CANCEL")  ) {
                if( !pMsg->get<string>("data.transactionId", transactionId) ) {
                    o << "{\"success\": false, \"reason\": \"cancel request is missing transaction id\"}" ;
                    client->sendResponse( rid, o.str() ) ;
                    return false ;
                }
                return m_pController->getDialogController()->sendCancelRequest( pMsg, rid ) ;
            }
            return m_pController->getDialogController()->sendRequestOutsideDialog( pMsg, rid ) ;
        }
    }
    void ClientController::sendResponseToClient( const string& rid, const string& strData ) {
        string null;
        sendResponseToClient( rid, strData, null) ;
    }
    void ClientController::sendResponseToClient( const string& rid, const string& strData, const string& transactionId ) {
        client_ptr client = findClientForRequest( rid ) ;
        if( !client ) {
            DR_LOG(log_warning) << "ClientController::sendResponseToClient - client that sent the request has disconnected: " << rid << endl ;
            return ;
        }
        m_ioservice.post( boost::bind(&Client::sendResponse, client, rid, strData) ) ;
        if( !transactionId.empty() ) {
            m_mapTransactions.insert( mapTransactions::value_type( transactionId, client) ) ; //TODO: need to think about when this gets cleared
        }
    }
    bool ClientController::route_cancel_transaction( nta_incoming_t* irq, sip_t const *sip, const string& transactionId ) {
        client_ptr client = findClientForTransaction( transactionId ) ;
        if( !client ) {
            DR_LOG(log_warning) << "ClientController::route_cancel_transaction - client that was sent the transaction has disconnected: " << transactionId << endl ;
            return false;
        }
        boost::shared_ptr<SofiaMsg> sm = boost::make_shared<SofiaMsg>( irq, sip ) ;
        string json = sm->str() ;
        JsonMsg jmsg( json ) ;

        m_ioservice.post( boost::bind(&Client::sendCancelTransaction, client, transactionId, json) ) ;

        return true ;

    }
    client_ptr ClientController::findClientForDialog( const string& dialogId ) {
        client_ptr client ;
        boost::lock_guard<boost::mutex> l( m_lock ) ;
        mapDialogs::iterator it = m_mapDialogs.find( dialogId ) ;
        if( m_mapDialogs.end() != it ) client = it->second.lock() ;
        return client ;
    }
    client_ptr ClientController::findClientForRequest( const string& rid ) {
        client_ptr client ;
        mapRequests::iterator it = m_mapRequests.find( rid ) ;
        if( m_mapRequests.end() != it ) client = it->second.lock() ;
        return client ;
    }
    client_ptr ClientController::findClientForTransaction( const string& transactionId ) {
        client_ptr client ;
        mapTransactions::iterator it = m_mapTransactions.find( transactionId ) ;
        if( m_mapTransactions.end() != it ) client = it->second.lock() ;
        return client ;
    }

    void ClientController::stop() {
        m_acceptor.cancel() ;
        m_ioservice.stop() ;
        m_thread.join() ;
    }

 }
 <commit_msg>newline fix<commit_after>/*
Copyright (c) 2013, David C Horton

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <iostream>

#include <boost/bind.hpp>
#include <boost/tokenizer.hpp>
#include <boost/foreach.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/asio.hpp>

#include "sofia-msg.hpp"
#include "client-controller.hpp"
#include "controller.hpp"

namespace drachtio {

    ClientController::ClientController( DrachtioController* pController, string& address, unsigned int port ) :
        m_pController( pController ),
        m_endpoint(  boost::asio::ip::tcp::v4(), port ),
        m_acceptor( m_ioservice, m_endpoint ) {

        boost::thread t(&ClientController::threadFunc, this) ;
        m_thread.swap( t ) ;

        this->start_accept() ;
    }
    ClientController::~ClientController() {
        this->stop() ;
   }
    void ClientController::onTimer( const boost::system::error_code& e, boost::asio::deadline_timer* t ) {
        //m_pController->processWatchdogTimer() ;
        //t->expires_at(t->expires_at() + boost::posix_time::seconds(25));
        //t->async_wait(boost::bind(&ClientController::onTimer, this, boost::asio::placeholders::error, t ));
    }

    void ClientController::threadFunc() {

        DR_LOG(log_debug) << "Client controller thread id: " << boost::this_thread::get_id() << endl ;

        boost::asio::deadline_timer t(m_ioservice, boost::posix_time::seconds(25) ) ;
        t.async_wait(boost::bind(&ClientController::onTimer, this, boost::asio::placeholders::error, &t ));

        /* to make sure the event loop doesn't terminate when there is no work to do */
        boost::asio::io_service::work work(m_ioservice);

        for(;;) {

            try {
                DR_LOG(log_notice) << "ClientController: io_service run loop started" << endl ;
                m_ioservice.run() ;
                DR_LOG(log_notice) << "ClientController: io_service run loop ended normally" << endl ;
                break ;
            }
            catch( std::exception& e) {
                DR_LOG(log_error) << "Error in event thread: " << string( e.what() ) << endl ;
                break ;
            }
        }
    }
    void ClientController::join( client_ptr client ) {
        m_clients.insert( client ) ;
        client_weak_ptr p( client ) ;
        for( vector<string>::const_iterator it = client->getServices().begin(); it != client->getServices().end(); ++it ) {
           m_services.insert(map_of_services::value_type(*it, p)) ;
        }
       DR_LOG(log_debug) << "Added client, count of connected clients is now: " << m_clients.size() << endl ;
    }
    void ClientController::leave( client_ptr client ) {
        m_clients.erase( client ) ;
        DR_LOG(log_debug) << "Removed client, count of connected clients is now: " << m_clients.size() << endl ;
    }

	void ClientController::start_accept() {
		client_ptr new_session( new Client( m_ioservice, *this ) ) ;
		m_acceptor.async_accept( new_session->socket(), boost::bind(&ClientController::accept_handler, this, new_session, boost::asio::placeholders::error));
    }
	void ClientController::accept_handler( client_ptr session, const boost::system::error_code& ec) {
        if(!ec) {
            session->start() ;
        }
        start_accept();
    }
    bool ClientController::wants_requests( client_ptr client, const string& verb ) {
        RequestSpecifier spec( client ) ;
        boost::lock_guard<boost::mutex> l( m_lock ) ;
        m_request_types.insert( map_of_request_types::value_type(verb, spec)) ;
        DR_LOG(log_debug) << "Added client for " << verb << " requests" << endl ;
        //TODO: validate the verb is supported
        return true ;
    }

    bool ClientController::route_request_outside_dialog( nta_incoming_t* irq, sip_t const *sip, const string& transactionId ) {

        boost::shared_ptr<SofiaMsg> sm = boost::make_shared<SofiaMsg>( irq, sip ) ;

        string method_name = sip->sip_request->rq_method_name ;
        transform(method_name.begin(), method_name.end(), method_name.begin(), ::tolower);

        boost::lock_guard<boost::mutex> l( m_lock ) ;
        client_ptr client ;
        string matchId ;
        pair<map_of_request_types::iterator,map_of_request_types::iterator> pair = m_request_types.equal_range( method_name ) ;
        for( map_of_request_types::iterator it = pair.first; it != pair.second; ) {
            RequestSpecifier& spec = it->second ;

            if( !spec.client() ) {
                /* note: our weak pointer may be to a client that has disconnected, so we need to check and remove them from the map
                    at this point if that is so.
                */
                DR_LOG(log_debug) << "Removing disconnected client while iterating" << endl ;
                m_request_types.erase(it++) ;
                continue ;
            }
            client = spec.client() ;
            break ;
        }

        if( !client ) {
            DR_LOG(log_info) << "No clients found to handle incoming " << method_name << " request" << endl ;
           return false ;
        }

        /* we've selected a client for this message */
        string json = sm->str() ;

        JsonMsg jmsg( json ) ;

        m_mapTransactions.insert( mapTransactions::value_type(transactionId,client)) ;

        m_ioservice.post( boost::bind(&Client::sendRequestOutsideDialog, client, transactionId, json) ) ;

        return true ;
    }
    void ClientController::respondToSipRequest( const string& transactionId, boost::shared_ptr<JsonMsg> pMsg ) {
         m_pController->getDialogController()->respondToSipRequest( transactionId, pMsg ) ;
    }

    bool ClientController::route_request_inside_dialog( nta_incoming_t* irq, sip_t const *sip, const string& dialogId ) {
        client_ptr client = this->findClientForDialog( dialogId );
        if( !client ) {
            DR_LOG(log_warning) << "ClientController::route_request_inside_dialog - client managing dialog has disconnected: " << dialogId << endl ;
            return false ;
        }

        boost::shared_ptr<SofiaMsg> sm = boost::make_shared<SofiaMsg>( irq, sip ) ;
        string json = sm->str() ;
        JsonMsg jmsg( json ) ;

        m_ioservice.post( boost::bind(&Client::sendRequestWithinDialog, client, dialogId, json) ) ;

        return true ;
    }
    bool ClientController::route_response_inside_transaction( nta_outgoing_t* orq, sip_t const *sip, const string& transactionId ) {
        client_ptr client = this->findClientForTransaction( transactionId );
        if( !client ) {
            DR_LOG(log_warning) << "ClientController::route_response_inside_transaction - client managing transaction has disconnected: " << transactionId << endl ;
            return false ;
        }

        boost::shared_ptr<SofiaMsg> sm = boost::make_shared<SofiaMsg>( orq, sip ) ;
        string json = sm->str() ;
        JsonMsg jmsg( json ) ;

        m_ioservice.post( boost::bind(&Client::sendResponseWithinTransaction, client, transactionId, json) ) ;

        return true ;
    }
    void ClientController::addDialogForTransaction( const string& transactionId, const string& dialogId ) {
        mapTransactions::iterator it = m_mapTransactions.find( transactionId ) ;
        if( m_mapTransactions.end() != it ) {
            m_mapDialogs.insert( mapDialogs::value_type(dialogId, it->second ) ) ;
        }
        else {
            DR_LOG(log_error) << "ClientController::addDialogForTransaction - transaction id " << transactionId << " not found" << endl ;
            assert(false) ;
        }
        DR_LOG(log_debug) << "ClientController::addDialogForTransaction - transaction id " << transactionId <<
            " has associated dialog " << dialogId << endl ;

        client_ptr client = this->findClientForDialog( dialogId );
        if( !client ) {
            m_mapDialogs.erase( dialogId ) ;
            m_mapTransactions.erase( transactionId ) ;
            DR_LOG(log_warning) << "ClientController::addDialogForTransaction - client managing dialog has disconnected: " << dialogId << endl ;
            return  ;
        }

        m_ioservice.post( boost::bind(&Client::sendDialogInfo, client, dialogId, transactionId) ) ;

    }
    bool ClientController::sendSipRequest( client_ptr client, boost::shared_ptr<JsonMsg> pMsg, const string& rid ) {
        ostringstream o ;
        m_mapRequests.insert( mapRequests::value_type( rid, client)) ;
        string strDialogId ;
        if( pMsg->get<string>("data.dialogId", strDialogId) ) {
            if( m_pController->sendRequestInsideDialog( pMsg, rid ) < 0 ) {
                o << "{\"success\": false, \"reason\": \"unknown sip dialog\"}" ;
                client->sendResponse( rid, o.str() ) ;
                return false ;
            }
            return true ;
        }
        else {
            string method ;
            string transactionId ;
            pMsg->get<string>("data.method", method ) ;
            if( 0 == method.compare("CANCEL")  ) {
                if( !pMsg->get<string>("data.transactionId", transactionId) ) {
                    o << "{\"success\": false, \"reason\": \"cancel request is missing transaction id\"}" ;
                    client->sendResponse( rid, o.str() ) ;
                    return false ;
                }
                return m_pController->getDialogController()->sendCancelRequest( pMsg, rid ) ;
            }
            return m_pController->getDialogController()->sendRequestOutsideDialog( pMsg, rid ) ;
        }
    }
    void ClientController::sendResponseToClient( const string& rid, const string& strData ) {
        string null;
        sendResponseToClient( rid, strData, null) ;
    }
    void ClientController::sendResponseToClient( const string& rid, const string& strData, const string& transactionId ) {
        client_ptr client = findClientForRequest( rid ) ;
        if( !client ) {
            DR_LOG(log_warning) << "ClientController::sendResponseToClient - client that sent the request has disconnected: " << rid << endl ;
            return ;
        }
        m_ioservice.post( boost::bind(&Client::sendResponse, client, rid, strData) ) ;
        if( !transactionId.empty() ) {
            m_mapTransactions.insert( mapTransactions::value_type( transactionId, client) ) ; //TODO: need to think about when this gets cleared
        }
    }
    bool ClientController::route_cancel_transaction( nta_incoming_t* irq, sip_t const *sip, const string& transactionId ) {
        client_ptr client = findClientForTransaction( transactionId ) ;
        if( !client ) {
            DR_LOG(log_warning) << "ClientController::route_cancel_transaction - client that was sent the transaction has disconnected: " << transactionId << endl ;
            return false;
        }
        boost::shared_ptr<SofiaMsg> sm = boost::make_shared<SofiaMsg>( irq, sip ) ;
        string json = sm->str() ;
        JsonMsg jmsg( json ) ;

        m_ioservice.post( boost::bind(&Client::sendCancelTransaction, client, transactionId, json) ) ;

        return true ;

    }
    client_ptr ClientController::findClientForDialog( const string& dialogId ) {
        client_ptr client ;
        boost::lock_guard<boost::mutex> l( m_lock ) ;
        mapDialogs::iterator it = m_mapDialogs.find( dialogId ) ;
        if( m_mapDialogs.end() != it ) client = it->second.lock() ;
        return client ;
    }
    client_ptr ClientController::findClientForRequest( const string& rid ) {
        client_ptr client ;
        mapRequests::iterator it = m_mapRequests.find( rid ) ;
        if( m_mapRequests.end() != it ) client = it->second.lock() ;
        return client ;
    }
    client_ptr ClientController::findClientForTransaction( const string& transactionId ) {
        client_ptr client ;
        mapTransactions::iterator it = m_mapTransactions.find( transactionId ) ;
        if( m_mapTransactions.end() != it ) client = it->second.lock() ;
        return client ;
    }

    void ClientController::stop() {
        m_acceptor.cancel() ;
        m_ioservice.stop() ;
        m_thread.join() ;
    }

 }
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <stdio.h>
#include <vector>
#include <string.h>

int skip(const std::string& s1)
{
    if (s1.length()==0) return 1;
    if (s1.length()>1)
    {
        size_t comment=s1.find_first_of("//");
        if (comment==s1.find_first_not_of(" \t")&&comment!=std::string::npos) return 2; //The comments should be ignored
    }
    if (s1.find("Source list file")!=std::string::npos) return 3;       //Same as the list of source files
    if (s1.find("Modules list file")!=std::string::npos) return 4;      //Same as the list of modules
    if (s1.find("File:")!=std::string::npos) return 5;                  //Same as the full file paths.
    if (s1.find("pybind11::handle cl_type = cl;")!=std::string::npos) return 6;  //This could be optimized in binder
    if (s1.find("__cxx11")!=std::string::npos) return 7;                         //This looks like implementation dependent
    if (s1.find("assign")!=std::string::npos) return 8;                          // The assing is binded differently for some reasons
    return 0;
}
int COMPARE_ASCII_FILES(const std::string& f1,const std::string& f2)
{
    std::fstream file1(f1.c_str()), file2(f2.c_str());
    std::string string1, string2;
    int j1,j2;
    j1 = 0;
    j2 = 0;
    if (!file1.is_open()|| !file2.is_open())
    {
            std::cout << "Cannot open one of files" << f1<<f2<<"\n";
            return EXIT_FAILURE;
    }
    std::cout <<"Run comparison"<< "\n";
    while((!file1.eof())&&(!file2.eof()))
    {
        for (;;) {
            j1++;
            if (!std::getline(file1,string1)) break;
            if (skip(string1)==0) break;
        }
        for (;;) {
            j2++;
            if (!std::getline(file2,string2)) break;
            if (skip(string2)==0) break;
        }
        if(string1.compare(string2) != 0)
        {
            std::cout << j1<<"/"<<j2 << "-th strings are not equal" << f1<<f2<<"\n";
            std::cout << "   ->" << string1 << "<-\n";
            std::cout << "   ->" << string2 << "<-\n";
            return EXIT_FAILURE;
        }
    }
    return EXIT_SUCCESS;
}
int main(int argc, char** argv)
{
    if (argc!=3&&argc!=4) {
        std::cout<<"Wrong number of arguments"<<std::endl;
        return EXIT_FAILURE;
    }
    int status=COMPARE_ASCII_FILES(argv[1],argv[2]);
    if (argc==4)  { if (strcmp(argv[3],"--always-success")==0)  return EXIT_SUCCESS; }
    return status;
}
<commit_msg>Comments from MR review,4<commit_after>//
// Copyright (c) 2020 Andrii Verbytskyi <andrii.verbytskyi@mpp.mpg.de> for the binder project
//
// All rights reserved. Use of this source code is governed by a
// MIT license that can be found in the LICENSE file.
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <vector>
#include <string.h>

int skip(const std::string& s1)
{
    if (s1.length()==0) return 1;
    if (s1.length()>1)
    {
        size_t comment=s1.find_first_of("//");
        if (comment==s1.find_first_not_of(" \t")&&comment!=std::string::npos) return 2; //The comments should be ignored
    }
    if (s1.find("Source list file")!=std::string::npos) return 3;       //Same as the list of source files
    if (s1.find("Modules list file")!=std::string::npos) return 4;      //Same as the list of modules
    if (s1.find("File:")!=std::string::npos) return 5;                  //Same as the full file paths.
    if (s1.find("pybind11::handle cl_type = cl;")!=std::string::npos) return 6;  //This could be optimized in binder
    if (s1.find("__cxx11")!=std::string::npos) return 7;                         //This looks like implementation dependent
    if (s1.find("assign")!=std::string::npos) return 8;                          // The assing is binded differently for some reasons
    return 0;
}
int compare_text_files(const std::string& f1,const std::string& f2)
{
    std::fstream file1(f1.c_str()), file2(f2.c_str());
    std::string string1, string2;
    int j1,j2;
    j1 = 0;
    j2 = 0;
    if (!file1.is_open()|| !file2.is_open())
    {
            std::cout << "Cannot open one of files" << f1<<f2<<"\n";
            return EXIT_FAILURE;
    }
    std::cout <<"Run comparison"<< "\n";
    while((!file1.eof())&&(!file2.eof()))
    {
        for (;;) {
            j1++;
            if (!std::getline(file1,string1)) break;
            if (skip(string1)==0) break;
        }
        for (;;) {
            j2++;
            if (!std::getline(file2,string2)) break;
            if (skip(string2)==0) break;
        }
        if(string1.compare(string2) != 0)
        {
            std::cout << j1<<"/"<<j2 << "-th strings are not equal" << f1<<f2<<"\n";
            std::cout << "   ->" << string1 << "<-\n";
            std::cout << "   ->" << string2 << "<-\n";
            return EXIT_FAILURE;
        }
    }
    return EXIT_SUCCESS;
}
int main(int argc, char** argv)
{
    if (argc!=3&&argc!=4) {
        std::cout<<"Wrong number of arguments"<<std::endl;
        return EXIT_FAILURE;
    }
    int status=compare_text_files(argv[1],argv[2]);
    if (argc==4)  { if (strcmp(argv[3],"--always-success")==0)  return EXIT_SUCCESS; }
    return status;
}
<|endoftext|>


<commit_before>#include "ptbox.h"
#include "helper.h"

#include <dirent.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <sys/types.h>

#ifdef __FreeBSD__
#   include <sys/param.h>
#   include <sys/queue.h>
#   include <sys/socket.h>
#   include <sys/sysctl.h>
#   include <libprocstat.h>
#else
// No ASLR on FreeBSD... not as of 11.0, anyway
#   include <sys/personality.h>
#endif

#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__))
#   define FD_DIR "/dev/fd"
#else
#   define FD_DIR "/proc/self/fd"
#endif

inline unsigned int get_seccomp_arch(int type) {
    switch (type) {
#ifdef SCMP_ARCH_X86
        case DEBUGGER_X86:
        case DEBUGGER_X86_ON_X64:
            return SCMP_ARCH_X86;
#endif
#ifdef SCMP_ARCH_X86_64
        case DEBUGGER_X64:
            return SCMP_ARCH_X86_64;
#endif
#ifdef SCMP_ARCH_X32
        case DEBUGGER_X32:
            return SCMP_ARCH_X32;
#endif
#ifdef SCMP_ARCH_ARM
        case DEBUGGER_ARM:
            return SCMP_ARCH_ARM;
#endif
#ifdef SCMP_ARCH_AARCH64
        case DEBUGGER_ARM64:
            return SCMP_ARCH_AARCH64;
#endif
    }

    return 0;
}

pt_debugger *get_ptdebugger(int type) {
    switch (type) {
#ifdef HAS_DEBUGGER_X86
        case DEBUGGER_X86:
            return new pt_debugger_x86();
#endif
#ifdef HAS_DEBUGGER_X64
        case DEBUGGER_X64:
            return new pt_debugger_x64();
#endif
#ifdef HAS_DEBUGGER_X86_ON_X64
        case DEBUGGER_X86_ON_X64:
            return new pt_debugger_x86_on_x64();
#endif
#ifdef HAS_DEBUGGER_X32
        case DEBUGGER_X32:
            return new pt_debugger_x32();
#endif
#ifdef HAS_DEBUGGER_ARM
        case DEBUGGER_ARM:
            return new pt_debugger_arm();
#endif
#ifdef HAS_DEBUGGER_ARM64
        case DEBUGGER_ARM64:
            return new pt_debugger_arm64();
#endif
    }
    return NULL;
}

inline void setrlimit2(int resource, rlim_t cur, rlim_t max) {
    rlimit limit;
    limit.rlim_cur = cur;
    limit.rlim_max = max;
    setrlimit(resource, &limit);
}

inline void setrlimit2(int resource, rlim_t limit) {
    setrlimit2(resource, limit, limit);
}

int cptbox_child_run(const struct child_config *config) {
#ifndef __FreeBSD__
    // There is no ASLR on FreeBSD, but disable it elsewhere
    if (config->personality > 0)
        personality(config->personality);
#endif

    if (config->address_space)
        setrlimit2(RLIMIT_AS, config->address_space);

    if (config->memory)
        setrlimit2(RLIMIT_DATA, config->memory);

    if (config->cpu_time)
        setrlimit2(RLIMIT_CPU, config->cpu_time, config->cpu_time + 1);

    if (config->nproc >= 0)
        setrlimit2(RLIMIT_NPROC, config->nproc);

    if (config->fsize >= 0)
        setrlimit2(RLIMIT_FSIZE, config->fsize);

    if (config->dir && *config->dir)
        chdir(config->dir);

    setrlimit2(RLIMIT_STACK, RLIM_INFINITY);
    setrlimit2(RLIMIT_CORE, 0);

#ifdef PR_SET_NO_NEW_PRIVS  // Since Linux 3.5
    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
        return 202;
#endif

    if (config->stdin_ >= 0)  dup2(config->stdin_, 0);
    if (config->stdout_ >= 0) dup2(config->stdout_, 1);
    if (config->stderr_ >= 0) dup2(config->stderr_, 2);

    for (int i = 3; i <= config->max_fd; ++i)
        dup2(config->fds[i-3], i);

    cptbox_closefrom(config->max_fd + 1);

    if (ptrace_traceme())
        return 204;

    kill(getpid(), SIGSTOP);

#if PTBOX_SECCOMP
    if (config->trace_syscalls) {
        scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_TRACE(0));
        if (!ctx) return 203;

        unsigned int child_arch = get_seccomp_arch(config->debugger_type);

        if (seccomp_arch_exist(ctx, child_arch) == -EEXIST &&
            seccomp_arch_add(ctx, child_arch) != 0) {
            return 203;
        }

        for (int syscall = 0; syscall < MAX_SYSCALL; syscall++) {
            if (config->syscall_whitelist[syscall]) {
                seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscall, 0);
            }
        }

        if (seccomp_load(ctx)) return 203;
        seccomp_release(ctx);
    }
#endif

    execve(config->file, config->argv, config->envp);
    return 205;
}

// From python's _posixsubprocess
static int pos_int_from_ascii(char *name) {
    int num = 0;
    while (*name >= '0' && *name <= '9') {
        num = num * 10 + (*name - '0');
        ++name;
    }
    if (*name)
        return -1;  /* Non digit found, not a number. */
    return num;
}

static void cptbox_close_fd(int fd) {
    while (close(fd) < 0 && errno == EINTR);
}

static void cptbox_closefrom_brute(int lowfd) {
    int max_fd = sysconf(_SC_OPEN_MAX);
    if (max_fd < 0) max_fd = 16384;
    for (; lowfd <= max_fd; ++lowfd)
        cptbox_close_fd(lowfd);
}

static void cptbox_closefrom_dirent(int lowfd) {
    DIR *d = opendir(FD_DIR);
    dirent *dir;

    if (d) {
        int fd_dirent = dirfd(d);
        errno = 0;
        while ((dir = readdir(d))) {
            int fd = pos_int_from_ascii(dir->d_name);
            if (fd < lowfd || fd == fd_dirent) continue;
            cptbox_close_fd(fd);
            errno = 0;
        }
        if (errno) cptbox_closefrom_brute(lowfd);
        closedir(d);
    } else cptbox_closefrom_brute(lowfd);
}

// Borrowing some SYS_getdents64 magic from python's _posixsubprocess.
// Look there for explanation. We don't actually need O_CLOEXEC,
// since this process is single-threaded after fork, and could not
// possibly be exec'd before we close the fd. If it is, we have
// bigger problems than leaking the directory fd.
#ifdef __linux__
#include <sys/syscall.h>
#include <sys/stat.h>
#include <fcntl.h>

struct linux_dirent64 {
    unsigned long long d_ino;
    long long d_off;
    unsigned short d_reclen;
    unsigned char d_type;
    char d_name[256];
};

static void cptbox_closefrom_getdents(int lowfd) {
    int fd_dir = open(FD_DIR, O_RDONLY, 0);
    if (fd_dir == -1) {
        cptbox_closefrom_brute(lowfd);
    } else {
        char buffer[sizeof(struct linux_dirent64)];
        int bytes;
        while ((bytes = syscall(SYS_getdents64, fd_dir,
                                (struct linux_dirent64 *)buffer,
                                sizeof(buffer))) > 0) {
            struct linux_dirent64 *entry;
            int offset;
            for (offset = 0; offset < bytes; offset += entry->d_reclen) {
                int fd;
                entry = (struct linux_dirent64 *)(buffer + offset);
                if ((fd = pos_int_from_ascii(entry->d_name)) < 0)
                    continue;  /* Not a number. */
                if (fd != fd_dir && fd >= lowfd)
                    cptbox_close_fd(fd);
            }
        }
        close(fd_dir);
    }
}
#endif

void cptbox_closefrom(int lowfd) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 8
    closefrom(lowfd);
#elif defined(F_CLOSEM)
    fcntl(fd, F_CLOSEM, 0);
#elif defined(__linux__)
    cptbox_closefrom_getdents(lowfd);
#else
    cptbox_closefrom_dirent(lowfd);
#endif
}

char *bsd_get_proc_fd(pid_t pid, int fdflags, int fdno) {
#ifdef __FreeBSD__
    int err = 0;
    char *buf = NULL;

    unsigned kp_cnt;
    struct procstat *procstat;
    struct kinfo_proc *kp;
    struct filestat_list *head;
    struct filestat *fst;

    procstat = procstat_open_sysctl();
    if (procstat) {
        kp = procstat_getprocs(procstat, KERN_PROC_PID, pid, &kp_cnt);
        if (kp) {
            head = procstat_getfiles(procstat, kp, 0);
            if (head) {
                err = EPERM; // Most likely you have no access
                STAILQ_FOREACH(fst, head, next) {
                    if ((fdflags && fst->fs_uflags & fdflags) ||
                       (!fdflags && fst->fs_fd == fdno)) {
                        buf = (char*) malloc(strlen(fst->fs_path) + 1);
                        if (buf)
                            strcpy(buf, fst->fs_path);
                        err = buf ? 0 : ENOMEM;
                        break;
                    }
                }
            } else err = errno;
            procstat_freeprocs(procstat, kp);
        } else err = errno;
        procstat_close(procstat);
        errno = err;
    }
    return buf;
#else
    errno = EOPNOTSUPP;
    return NULL;
#endif
}

char *bsd_get_proc_cwd(pid_t pid) {
#ifdef __FreeBSD__
    return bsd_get_proc_fd(pid, PS_FST_UFLAG_CDIR, 0);
#else
    errno = EOPNOTSUPP;
    return NULL;
#endif
}

char *bsd_get_proc_fdno(pid_t pid, int fdno) {
    return bsd_get_proc_fd(pid, 0, fdno);
}
<commit_msg>cptbox: Disable Speculative Store Bypass mitigations on submissions<commit_after>#include "ptbox.h"
#include "helper.h"

#include <dirent.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <sys/types.h>

#ifdef __FreeBSD__
#   include <sys/param.h>
#   include <sys/queue.h>
#   include <sys/socket.h>
#   include <sys/sysctl.h>
#   include <libprocstat.h>
#else
// No ASLR on FreeBSD... not as of 11.0, anyway
#   include <sys/personality.h>
#endif

#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__))
#   define FD_DIR "/dev/fd"
#else
#   define FD_DIR "/proc/self/fd"
#endif

inline unsigned int get_seccomp_arch(int type) {
    switch (type) {
#ifdef SCMP_ARCH_X86
        case DEBUGGER_X86:
        case DEBUGGER_X86_ON_X64:
            return SCMP_ARCH_X86;
#endif
#ifdef SCMP_ARCH_X86_64
        case DEBUGGER_X64:
            return SCMP_ARCH_X86_64;
#endif
#ifdef SCMP_ARCH_X32
        case DEBUGGER_X32:
            return SCMP_ARCH_X32;
#endif
#ifdef SCMP_ARCH_ARM
        case DEBUGGER_ARM:
            return SCMP_ARCH_ARM;
#endif
#ifdef SCMP_ARCH_AARCH64
        case DEBUGGER_ARM64:
            return SCMP_ARCH_AARCH64;
#endif
    }

    return 0;
}

pt_debugger *get_ptdebugger(int type) {
    switch (type) {
#ifdef HAS_DEBUGGER_X86
        case DEBUGGER_X86:
            return new pt_debugger_x86();
#endif
#ifdef HAS_DEBUGGER_X64
        case DEBUGGER_X64:
            return new pt_debugger_x64();
#endif
#ifdef HAS_DEBUGGER_X86_ON_X64
        case DEBUGGER_X86_ON_X64:
            return new pt_debugger_x86_on_x64();
#endif
#ifdef HAS_DEBUGGER_X32
        case DEBUGGER_X32:
            return new pt_debugger_x32();
#endif
#ifdef HAS_DEBUGGER_ARM
        case DEBUGGER_ARM:
            return new pt_debugger_arm();
#endif
#ifdef HAS_DEBUGGER_ARM64
        case DEBUGGER_ARM64:
            return new pt_debugger_arm64();
#endif
    }
    return NULL;
}

inline void setrlimit2(int resource, rlim_t cur, rlim_t max) {
    rlimit limit;
    limit.rlim_cur = cur;
    limit.rlim_max = max;
    setrlimit(resource, &limit);
}

inline void setrlimit2(int resource, rlim_t limit) {
    setrlimit2(resource, limit, limit);
}

int cptbox_child_run(const struct child_config *config) {
#ifndef __FreeBSD__
    // There is no ASLR on FreeBSD, but disable it elsewhere
    if (config->personality > 0)
        personality(config->personality);
#endif

    if (config->address_space)
        setrlimit2(RLIMIT_AS, config->address_space);

    if (config->memory)
        setrlimit2(RLIMIT_DATA, config->memory);

    if (config->cpu_time)
        setrlimit2(RLIMIT_CPU, config->cpu_time, config->cpu_time + 1);

    if (config->nproc >= 0)
        setrlimit2(RLIMIT_NPROC, config->nproc);

    if (config->fsize >= 0)
        setrlimit2(RLIMIT_FSIZE, config->fsize);

    if (config->dir && *config->dir)
        chdir(config->dir);

    setrlimit2(RLIMIT_STACK, RLIM_INFINITY);
    setrlimit2(RLIMIT_CORE, 0);

#ifdef PR_SET_NO_NEW_PRIVS  // Since Linux 3.5
    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
        return 202;
#endif

#ifdef PR_SET_SPECULATION_CTRL  // Since Linux 4.17
    // Turn off Spectre Variant 4 protection in case it is turned on; we don't
    // care if submissions shoot themselves in the foot. Let this be a
    // best-effort attempt, and don't stop the submission from running if the
    // prctl fails.
    prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_ENABLE, 0, 0);
#endif

    if (config->stdin_ >= 0)  dup2(config->stdin_, 0);
    if (config->stdout_ >= 0) dup2(config->stdout_, 1);
    if (config->stderr_ >= 0) dup2(config->stderr_, 2);

    for (int i = 3; i <= config->max_fd; ++i)
        dup2(config->fds[i-3], i);

    cptbox_closefrom(config->max_fd + 1);

    if (ptrace_traceme())
        return 204;

    kill(getpid(), SIGSTOP);

#if PTBOX_SECCOMP
    if (config->trace_syscalls) {
        scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_TRACE(0));
        if (!ctx) return 203;

        unsigned int child_arch = get_seccomp_arch(config->debugger_type);

        if (seccomp_arch_exist(ctx, child_arch) == -EEXIST &&
            seccomp_arch_add(ctx, child_arch) != 0) {
            return 203;
        }

        for (int syscall = 0; syscall < MAX_SYSCALL; syscall++) {
            if (config->syscall_whitelist[syscall]) {
                seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscall, 0);
            }
        }

        if (seccomp_load(ctx)) return 203;
        seccomp_release(ctx);
    }
#endif

    execve(config->file, config->argv, config->envp);
    return 205;
}

// From python's _posixsubprocess
static int pos_int_from_ascii(char *name) {
    int num = 0;
    while (*name >= '0' && *name <= '9') {
        num = num * 10 + (*name - '0');
        ++name;
    }
    if (*name)
        return -1;  /* Non digit found, not a number. */
    return num;
}

static void cptbox_close_fd(int fd) {
    while (close(fd) < 0 && errno == EINTR);
}

static void cptbox_closefrom_brute(int lowfd) {
    int max_fd = sysconf(_SC_OPEN_MAX);
    if (max_fd < 0) max_fd = 16384;
    for (; lowfd <= max_fd; ++lowfd)
        cptbox_close_fd(lowfd);
}

static void cptbox_closefrom_dirent(int lowfd) {
    DIR *d = opendir(FD_DIR);
    dirent *dir;

    if (d) {
        int fd_dirent = dirfd(d);
        errno = 0;
        while ((dir = readdir(d))) {
            int fd = pos_int_from_ascii(dir->d_name);
            if (fd < lowfd || fd == fd_dirent) continue;
            cptbox_close_fd(fd);
            errno = 0;
        }
        if (errno) cptbox_closefrom_brute(lowfd);
        closedir(d);
    } else cptbox_closefrom_brute(lowfd);
}

// Borrowing some SYS_getdents64 magic from python's _posixsubprocess.
// Look there for explanation. We don't actually need O_CLOEXEC,
// since this process is single-threaded after fork, and could not
// possibly be exec'd before we close the fd. If it is, we have
// bigger problems than leaking the directory fd.
#ifdef __linux__
#include <sys/syscall.h>
#include <sys/stat.h>
#include <fcntl.h>

struct linux_dirent64 {
    unsigned long long d_ino;
    long long d_off;
    unsigned short d_reclen;
    unsigned char d_type;
    char d_name[256];
};

static void cptbox_closefrom_getdents(int lowfd) {
    int fd_dir = open(FD_DIR, O_RDONLY, 0);
    if (fd_dir == -1) {
        cptbox_closefrom_brute(lowfd);
    } else {
        char buffer[sizeof(struct linux_dirent64)];
        int bytes;
        while ((bytes = syscall(SYS_getdents64, fd_dir,
                                (struct linux_dirent64 *)buffer,
                                sizeof(buffer))) > 0) {
            struct linux_dirent64 *entry;
            int offset;
            for (offset = 0; offset < bytes; offset += entry->d_reclen) {
                int fd;
                entry = (struct linux_dirent64 *)(buffer + offset);
                if ((fd = pos_int_from_ascii(entry->d_name)) < 0)
                    continue;  /* Not a number. */
                if (fd != fd_dir && fd >= lowfd)
                    cptbox_close_fd(fd);
            }
        }
        close(fd_dir);
    }
}
#endif

void cptbox_closefrom(int lowfd) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 8
    closefrom(lowfd);
#elif defined(F_CLOSEM)
    fcntl(fd, F_CLOSEM, 0);
#elif defined(__linux__)
    cptbox_closefrom_getdents(lowfd);
#else
    cptbox_closefrom_dirent(lowfd);
#endif
}

char *bsd_get_proc_fd(pid_t pid, int fdflags, int fdno) {
#ifdef __FreeBSD__
    int err = 0;
    char *buf = NULL;

    unsigned kp_cnt;
    struct procstat *procstat;
    struct kinfo_proc *kp;
    struct filestat_list *head;
    struct filestat *fst;

    procstat = procstat_open_sysctl();
    if (procstat) {
        kp = procstat_getprocs(procstat, KERN_PROC_PID, pid, &kp_cnt);
        if (kp) {
            head = procstat_getfiles(procstat, kp, 0);
            if (head) {
                err = EPERM; // Most likely you have no access
                STAILQ_FOREACH(fst, head, next) {
                    if ((fdflags && fst->fs_uflags & fdflags) ||
                       (!fdflags && fst->fs_fd == fdno)) {
                        buf = (char*) malloc(strlen(fst->fs_path) + 1);
                        if (buf)
                            strcpy(buf, fst->fs_path);
                        err = buf ? 0 : ENOMEM;
                        break;
                    }
                }
            } else err = errno;
            procstat_freeprocs(procstat, kp);
        } else err = errno;
        procstat_close(procstat);
        errno = err;
    }
    return buf;
#else
    errno = EOPNOTSUPP;
    return NULL;
#endif
}

char *bsd_get_proc_cwd(pid_t pid) {
#ifdef __FreeBSD__
    return bsd_get_proc_fd(pid, PS_FST_UFLAG_CDIR, 0);
#else
    errno = EOPNOTSUPP;
    return NULL;
#endif
}

char *bsd_get_proc_fdno(pid_t pid, int fdno) {
    return bsd_get_proc_fd(pid, 0, fdno);
}
<|endoftext|>


<commit_before>/**
 * @file Cosa/SPI/Driver/SRPO.hh
 * @version 1.0
 *
 * @section License
 * Copyright (C) 2014, Mikael Patel
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * This file is part of the Arduino Che Cosa project.
 */

#ifndef COSA_SPI_DRIVER_SRPO_HH
#define COSA_SPI_DRIVER_SRPO_HH

#include "Cosa/SPI.hh"

/**
 * N-Shift Register Parallel Output, 3-Wire SPI device driver. The
 * shift registers (74HC595) may be cascaded for N*8-bit parallel
 * output port (see circuit below). The pins are numbered from the
 * first connect shift register (Q0..Q7) and updwards in the chain
 * (Q8..Q15) and so on.
 *
 * @section Circuit
 * @code
 *                         74HC595    (VCC)
 *                       +----U----+    |
 * (Q1)----------------1-|Q1    VCC|-16-+
 * (Q2)----------------2-|Q2     Q0|-15------------(Q0)
 * (Q3)----------------3-|Q3    SER|-14------(MOSI/D11)
 * (Q4)----------------4-|Q4    /OE|-13-----------(GND)
 * (Q5)----------------5-|Q5   RCLK|-12--------(EN/D10)------+
 * (Q6)----------------6-|Q6   SCLK|-11-------(SCK/D13)----+ |
 * (Q7)----------------7-|Q7    /MR|-10-----------(VCC)    | |
 *                   +-8-|GND   Q7S|--9------------------+ | |
 *                   |   +---------+                     | | |
 *                   |      0.1uF                        | | |
 *                 (GND)-----||-------(VCC)              | | |
 *                                      |                | | |
 *                         74HC595      |                | | |
 *                       +----U----+    |                | | |
 * (Q9)----------------1-|Q1    VCC|-16-+                | | |
 * (Q10)---------------2-|Q2     Q0|-15------------(Q8)  | | |
 * (Q11)---------------3-|Q3    SER|-14------------------+ | |
 * (Q12)---------------4-|Q4    /OE|-13-----------(GND)    | |
 * (Q13)---------------5-|Q5   RCLK|-12--------------------(-+
 * (Q14)---------------6-|Q6   SCLK|-11--------------------+ |
 * (Q15)---------------7-|Q7    /MR|-10-----------(VCC)    | |
 *                   +-8-|GND   Q7S|--9------------------+ | |
 *                   |   +---------+                     | | |
 *                   |      0.1uF                        | | |
 *                 (GND)-----||-------(VCC)              | | |
 *                                      |                | | |
 *                                      V                V V V
 * @endcode
 *
 * @section Note
 * The shift registers will clock data for presented on the SPI bus
 * (MOSI/SCK) but will not transfer to output register until the
 * enable pulse is given (i.e. when addressed).
 *
 * @param[in] N number of shift registers (N * 8 output pins).
 */
template<uint8_t N>
class SRPO : public SPI::Driver {
public:
  /** Number of pins for N ports */
  static const uint8_t PINS = N * CHARBITS;

  /**
   * Construct N-shift register connected to SPI (MOSI, SCL) and given
   * chip select pin.
   * @param[in] cs chip select pin (Default Board::D10/D3).
   * @param[in] clock SPI hardware setting (default DIV4_CLOCK).
   */
#if !defined(BOARD_ATTINY)
  SRPO(Board::DigitalPin cs = Board::D10,
       SPI::Clock rate = SPI::DEFAULT_CLOCK) :
    SPI::Driver(cs, SPI::PULSE_HIGH, rate)
  {
    clear();
    update();
  }
#else
  SRPO(Board::DigitalPin cs = Board::D3,
       SPI::Clock rate = SPI::DEFAULT_CLOCK) :
    SPI::Driver(cs, SPI::PULSE_HIGH, rate)
  {
    clear();
    update();
  }
#endif

  /**
   * Return true(1) if the given pin in shadow register is set,
   * otherwise false(0).
   * @param[in] pin pin number.
   * @return bool.
   */
  bool is_set(uint8_t pin)
    __attribute__((always_inline))
  {
    uint8_t ix = (pin >> 3);
    return ((m_port[ix] & _BV(pin & 0x7)) != 0);
  }

  /**
   * Return true(1) if the given pin in shadow register is set,
   * otherwise false(0).
   * @param[in] pin pin number.
   * @return bool.
   */
  void is_clear(uint8_t pin)
    __attribute__((always_inline))
  {
    uint8_t ix = (pin >> 3);
    return ((m_port[ix] & _BV(pin & 0x7)) == 0);
  }

  /**
   * Set given pin in shadow register. Call update() to write to shift
   * register.
   * @param[in] pin pin number.
   */
  void set(uint8_t pin)
    __attribute__((always_inline))
  {
    uint8_t ix = (pin >> 3);
    m_port[ix] |= _BV(pin & 0x7);
  }

  /**
   * Clear given pin in shadow register. Call update() to write to shift
   * register.
   * @param[in] pin pin number.
   */
  void clear(uint8_t pin)
    __attribute__((always_inline))
  {
    uint8_t ix = (pin >> 3);
    m_port[ix] &= ~_BV(pin & 0x7);
  }

  /**
   * Toggle given pin in shadow register. Call update() to write to shift
   * register.
   * @param[in] pin pin number.
   */
  void toggle(uint8_t pin)
    __attribute__((always_inline))
  {
    if (is_set(pin))
      clear(pin);
    else
      set(pin);
  }

  /**
   * Set the shadow registers. Call update() to write to shift
   * register.
   */
  void set()
    __attribute__((always_inline))
  {
    memset(m_port, 0xff, N);
  }

  /**
   * Clear the shadow registers. Call update() to write to shift
   * register.
   */
  void clear()
    __attribute__((always_inline))
  {
    memset(m_port, 0, N);
  }

  /**
   * Update shift register with value of shadow registers.
   */
  void update()
  {
    spi.begin(this);
    uint8_t ix = N - 1;
    spi.transfer_start(m_port[ix]);
    while (ix--) {
      uint8_t data = m_port[ix];
      spi.transfer_await();
      spi.transfer_start(data);
    }
    spi.transfer_await();
    spi.end();
  }

  /**
   * Output pin in shift-register parallel output port.
   */
  class OutputPin {
  public:
    OutputPin(SRPO<N>* srpo, uint8_t pin) :
      m_srpo(srpo),
      m_pin(pin)
    {
    }

    /**
     * Set pin in shadow register. Call update() to write to shift
     * register.
     */
    void set()
      __attribute__((always_inline))
    {
      m_srpo->set(m_pin);
    }

    /**
     * Clear pin in shadow register. Call update() to write to shift
     * register.
     */
    void clear()
      __attribute__((always_inline))
    {
      m_srpo->clear(m_pin);
    }

    /**
     * Toggle pin in shadow register. Call update() to write to shift
     * register.
     */
    void toggle()
      __attribute__((always_inline))
    {
      m_srpo->toggle(m_pin);
    }

  protected:
    SRPO<N>* m_srpo;
    const uint8_t m_pin;
  };

protected:
  /** Shadow port register. */
  uint8_t m_port[N];
};
#endif
<commit_msg>Update SRPO driver; ripple effect of SPI acquire-release pattern.<commit_after>/**
 * @file Cosa/SPI/Driver/SRPO.hh
 * @version 1.0
 *
 * @section License
 * Copyright (C) 2014, Mikael Patel
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * This file is part of the Arduino Che Cosa project.
 */

#ifndef COSA_SPI_DRIVER_SRPO_HH
#define COSA_SPI_DRIVER_SRPO_HH

#include "Cosa/SPI.hh"

/**
 * N-Shift Register Parallel Output, 3-Wire SPI device driver. The
 * shift registers (74HC595) may be cascaded for N*8-bit parallel
 * output port (see circuit below). The pins are numbered from the
 * first connect shift register (Q0..Q7) and updwards in the chain
 * (Q8..Q15) and so on.
 *
 * @section Circuit
 * @code
 *                         74HC595    (VCC)
 *                       +----U----+    |
 * (Q1)----------------1-|Q1    VCC|-16-+
 * (Q2)----------------2-|Q2     Q0|-15------------(Q0)
 * (Q3)----------------3-|Q3    SER|-14------(MOSI/D11)
 * (Q4)----------------4-|Q4    /OE|-13-----------(GND)
 * (Q5)----------------5-|Q5   RCLK|-12--------(EN/D10)------+
 * (Q6)----------------6-|Q6   SCLK|-11-------(SCK/D13)----+ |
 * (Q7)----------------7-|Q7    /MR|-10-----------(VCC)    | |
 *                   +-8-|GND   Q7S|--9------------------+ | |
 *                   |   +---------+                     | | |
 *                   |      0.1uF                        | | |
 *                 (GND)-----||-------(VCC)              | | |
 *                                      |                | | |
 *                         74HC595      |                | | |
 *                       +----U----+    |                | | |
 * (Q9)----------------1-|Q1    VCC|-16-+                | | |
 * (Q10)---------------2-|Q2     Q0|-15------------(Q8)  | | |
 * (Q11)---------------3-|Q3    SER|-14------------------+ | |
 * (Q12)---------------4-|Q4    /OE|-13-----------(GND)    | |
 * (Q13)---------------5-|Q5   RCLK|-12--------------------(-+
 * (Q14)---------------6-|Q6   SCLK|-11--------------------+ |
 * (Q15)---------------7-|Q7    /MR|-10-----------(VCC)    | |
 *                   +-8-|GND   Q7S|--9------------------+ | |
 *                   |   +---------+                     | | |
 *                   |      0.1uF                        | | |
 *                 (GND)-----||-------(VCC)              | | |
 *                                      |                | | |
 *                                      V                V V V
 * @endcode
 *
 * @section Note
 * The shift registers will clock data for presented on the SPI bus
 * (MOSI/SCK) but will not transfer to output register until the
 * enable pulse is given (i.e. when addressed).
 *
 * @param[in] N number of shift registers (N * 8 output pins).
 */
template<uint8_t N>
class SRPO : public SPI::Driver {
public:
  /** Number of pins for N ports */
  static const uint8_t PINS = N * CHARBITS;

  /**
   * Construct N-shift register connected to SPI (MOSI, SCL) and given
   * chip select pin.
   * @param[in] cs chip select pin (Default Board::D10/D3).
   * @param[in] clock SPI hardware setting (default DIV4_CLOCK).
   */
#if !defined(BOARD_ATTINY)
  SRPO(Board::DigitalPin cs = Board::D10,
       SPI::Clock rate = SPI::DEFAULT_CLOCK) :
    SPI::Driver(cs, SPI::PULSE_HIGH, rate)
  {
    clear();
    update();
  }
#else
  SRPO(Board::DigitalPin cs = Board::D3,
       SPI::Clock rate = SPI::DEFAULT_CLOCK) :
    SPI::Driver(cs, SPI::PULSE_HIGH, rate)
  {
    clear();
    update();
  }
#endif

  /**
   * Return true(1) if the given pin in shadow register is set,
   * otherwise false(0).
   * @param[in] pin pin number.
   * @return bool.
   */
  bool is_set(uint8_t pin)
    __attribute__((always_inline))
  {
    uint8_t ix = (pin >> 3);
    return ((m_port[ix] & _BV(pin & 0x7)) != 0);
  }

  /**
   * Return true(1) if the given pin in shadow register is set,
   * otherwise false(0).
   * @param[in] pin pin number.
   * @return bool.
   */
  void is_clear(uint8_t pin)
    __attribute__((always_inline))
  {
    uint8_t ix = (pin >> 3);
    return ((m_port[ix] & _BV(pin & 0x7)) == 0);
  }

  /**
   * Set given pin in shadow register. Call update() to write to shift
   * register.
   * @param[in] pin pin number.
   */
  void set(uint8_t pin)
    __attribute__((always_inline))
  {
    uint8_t ix = (pin >> 3);
    m_port[ix] |= _BV(pin & 0x7);
  }

  /**
   * Clear given pin in shadow register. Call update() to write to shift
   * register.
   * @param[in] pin pin number.
   */
  void clear(uint8_t pin)
    __attribute__((always_inline))
  {
    uint8_t ix = (pin >> 3);
    m_port[ix] &= ~_BV(pin & 0x7);
  }

  /**
   * Toggle given pin in shadow register. Call update() to write to shift
   * register.
   * @param[in] pin pin number.
   */
  void toggle(uint8_t pin)
    __attribute__((always_inline))
  {
    if (is_set(pin))
      clear(pin);
    else
      set(pin);
  }

  /**
   * Set the shadow registers. Call update() to write to shift
   * register.
   */
  void set()
    __attribute__((always_inline))
  {
    memset(m_port, 0xff, N);
  }

  /**
   * Clear the shadow registers. Call update() to write to shift
   * register.
   */
  void clear()
    __attribute__((always_inline))
  {
    memset(m_port, 0, N);
  }

  /**
   * Update shift register with value of shadow registers.
   */
  void update()
  {
    spi.acquire(this);
      spi.begin();
        uint8_t ix = N - 1;
	spi.transfer_start(m_port[ix]);
	while (ix--) {
	  uint8_t data = m_port[ix];
	  spi.transfer_await();
	  spi.transfer_start(data);
	}
	spi.transfer_await();
      spi.end();
    spi.release();
  }

  /**
   * Output pin in shift-register parallel output port.
   */
  class OutputPin {
  public:
    OutputPin(SRPO<N>* srpo, uint8_t pin) :
      m_srpo(srpo),
      m_pin(pin)
    {
    }

    /**
     * Set pin in shadow register. Call update() to write to shift
     * register.
     */
    void set()
      __attribute__((always_inline))
    {
      m_srpo->set(m_pin);
    }

    /**
     * Clear pin in shadow register. Call update() to write to shift
     * register.
     */
    void clear()
      __attribute__((always_inline))
    {
      m_srpo->clear(m_pin);
    }

    /**
     * Toggle pin in shadow register. Call update() to write to shift
     * register.
     */
    void toggle()
      __attribute__((always_inline))
    {
      m_srpo->toggle(m_pin);
    }

  protected:
    SRPO<N>* m_srpo;
    const uint8_t m_pin;
  };

protected:
  /** Shadow port register. */
  uint8_t m_port[N];
};
#endif
<|endoftext|>


<commit_before>//! \file
/*
**  Copyright (C) - Triton
**
**  This program is under the terms of the BSD License.
*/

#include <callbacks.hpp>
#include <exceptions.hpp>

#ifdef TRITON_PYTHON_BINDINGS
  #include <pythonObjects.hpp>
  #include <pythonUtils.hpp>
  #include <pythonXFunctions.hpp>
#endif

namespace triton {
  namespace callbacks {

    Callbacks::Callbacks() {
      this->isDefined = false;
    }

    Callbacks::Callbacks(const Callbacks& copy) {
      #ifdef TRITON_PYTHON_BINDINGS
      this->pyUnmappedMemoryHitCallbacks        = copy.pyUnmappedMemoryHitCallbacks;
      this->pySymbolicSimplificationCallbacks   = copy.pySymbolicSimplificationCallbacks;
      #endif
      this->unmappedMemoryHitCallbacks          = copy.unmappedMemoryHitCallbacks;
      this->symbolicSimplificationCallbacks     = copy.symbolicSimplificationCallbacks;
      this->isDefined                           = copy.isDefined;
    }

    Callbacks::~Callbacks() {
    }

    void Callbacks::operator=(const Callbacks& copy) {
      #ifdef TRITON_PYTHON_BINDINGS
      this->pyUnmappedMemoryHitCallbacks        = copy.pyUnmappedMemoryHitCallbacks;
      this->pySymbolicSimplificationCallbacks   = copy.pySymbolicSimplificationCallbacks;
      #endif
      this->unmappedMemoryHitCallbacks          = copy.unmappedMemoryHitCallbacks;
      this->symbolicSimplificationCallbacks     = copy.symbolicSimplificationCallbacks;
      this->isDefined                           = copy.isDefined;
    }

    void Callbacks::addCallback(triton::callbacks::unmappedMemoryHitCallback cb) {
      this->unmappedMemoryHitCallbacks.push_back(cb);
      this->isDefined = true;
    }

    void Callbacks::addCallback(triton::callbacks::symbolicSimplificationCallback cb) {
      this->symbolicSimplificationCallbacks.push_back(cb);
      this->isDefined = true;
    }

    #ifdef TRITON_PYTHON_BINDINGS
    void Callbacks::addCallback(triton::callbacks::callback_e kind, PyObject* function) {
      switch (kind) {
        case UNMAPPED_MEMORY_HIT:
            this->pyUnmappedMemoryHitCallbacks.push_back(function);
          break;
        case SYMBOLIC_SIMPLIFICATION:
            this->pySymbolicSimplificationCallbacks.push_back(function);
          break;
        default:
          throw triton::exceptions::Callbacks("Callbacks::addCallback(): Invalid kind of callback.");
      };
      this->isDefined = true;
    }
    #endif

    void Callbacks::deleteCallback(triton::callbacks::unmappedMemoryHitCallback cb) {
      this->unmappedMemoryHitCallbacks.remove(cb);
      if (this->countCallbacks() == 0)
        this->isDefined = false;
    }

    void Callbacks::deleteCallback(triton::callbacks::symbolicSimplificationCallback cb) {
      this->symbolicSimplificationCallbacks.remove(cb);
      if (this->countCallbacks() == 0)
        this->isDefined = false;
    }

    #ifdef TRITON_PYTHON_BINDINGS
    void Callbacks::deleteCallback(triton::callbacks::callback_e kind, PyObject* function) {
      switch (kind) {
        case UNMAPPED_MEMORY_HIT:
            this->pyUnmappedMemoryHitCallbacks.remove(function);
          break;
        case SYMBOLIC_SIMPLIFICATION:
            this->pySymbolicSimplificationCallbacks.remove(function);
          break;
        default:
          throw triton::exceptions::Callbacks("Callbacks::deleteCallback(): Invalid kind of callback.");
      };

      if (this->countCallbacks() == 0)
        this->isDefined = false;
    }
    #endif

    triton::ast::AbstractNode* Callbacks::processCallbacks(triton::callbacks::callback_e kind, triton::ast::AbstractNode* node) const {
      switch (kind) {
        case triton::callbacks::SYMBOLIC_SIMPLIFICATION: {
          // C++ callbacks
          std::list<triton::callbacks::symbolicSimplificationCallback>::const_iterator it1;
          for (it1 = this->symbolicSimplificationCallbacks.begin(); it1 != this->symbolicSimplificationCallbacks.end(); it1++) {
            node = (*it1)(node);
            if (node == nullptr)
              throw triton::exceptions::Callbacks("Callbacks::processCallbacks(SYMBOLIC_SIMPLIFICATION): You cannot return a nullptr node.");
          }

          #ifdef TRITON_PYTHON_BINDINGS
          // Python callbacks
          std::list<PyObject*>::const_iterator it2;
          for (it2 = this->pySymbolicSimplificationCallbacks.begin(); it2 != this->pySymbolicSimplificationCallbacks.end(); it2++) {

            /* Create function args */
            PyObject* args = triton::bindings::python::xPyTuple_New(1);
            PyTuple_SetItem(args, 0, triton::bindings::python::PyAstNode(node));

            /* Call the callback */
            PyObject* ret = PyObject_CallObject(*it2, args);

            /* Check the call */
            if (ret == nullptr)
              throw triton::exceptions::Callbacks("Callbacks::processCallbacks(SYMBOLIC_SIMPLIFICATION): Fail to call the python callback.");

            /* Check if the callback has returned a AbstractNode */
            if (!PyAstNode_Check(ret))
              throw triton::exceptions::Callbacks("Callbacks::processCallbacks(SYMBOLIC_SIMPLIFICATION): You must return a AstNode object.");

            /* Update node */
            node = PyAstNode_AsAstNode(ret);
            Py_DECREF(args);
          }
          #endif
          break;
        }
        default:
          throw triton::exceptions::Callbacks("Callbacks::processCallbacks(): Invalid kind of callback for this C++ polymorphism.");
      };

      return node;
    }

    triton::uint8 Callbacks::processCallbacks(triton::callbacks::callback_e kind, triton::uint64 address) const {
      switch (kind) {
        case triton::callbacks::UNMAPPED_MEMORY_HIT: {
          triton::uint8 value = 0;

          // C++ callbacks
          std::list<triton::callbacks::unmappedMemoryHitCallback>::const_iterator it1;
          for (it1 = this->unmappedMemoryHitCallbacks.begin(); it1 != this->unmappedMemoryHitCallbacks.end(); it1++)
            value = (*it1)(address);

          #ifdef TRITON_PYTHON_BINDINGS
          // Python callbacks
          std::list<PyObject*>::const_iterator it2;
          for (it2 = this->pyUnmappedMemoryHitCallbacks.begin(); it2 != this->pyUnmappedMemoryHitCallbacks.end(); it2++) {

            /* Create function args */
            PyObject* args = triton::bindings::python::xPyTuple_New(1);
            PyTuple_SetItem(args, 0, triton::bindings::python::PyLong_FromUint64(address));

            /* Call the callback */
            PyObject* ret = PyObject_CallObject(*it2, args);

            /* Check the call */
            if (ret == nullptr)
              throw triton::exceptions::Callbacks("Callbacks::processCallbacks(UNMAPPED_MEMORY_HIT): Fail to call the python callback.");

            /* Check if the callback has returned an integer */
            if (!PyLong_Check(ret) || !PyInt_Check(ret))
              throw triton::exceptions::Callbacks("Callbacks::processCallbacks(UNMAPPED_MEMORY_HIT): You must return a Integer object.");

            value = static_cast<triton::uint8>(triton::bindings::python::PyLong_AsUint32(ret) & 0xff);
            Py_DECREF(args);
          }
          #endif
          return value;
        }
        default:
          throw triton::exceptions::Callbacks("Callbacks::processCallbacks(): Invalid kind of callback for this C++ polymorphism.");
      };
    }

    triton::uint32 Callbacks::countCallbacks(void) const {
      triton::uint32 count = 0;

      count += this->unmappedMemoryHitCallbacks.size();
      count += this->symbolicSimplificationCallbacks.size();
      #ifdef TRITON_PYTHON_BINDINGS
      count += this->pyUnmappedMemoryHitCallbacks.size();
      count += this->pySymbolicSimplificationCallbacks.size();
      #endif

      return count;
    }

  }; /* callbacks namespace */
}; /* triton namespace */
<commit_msg>#318 in progress<commit_after>//! \file
/*
**  Copyright (C) - Triton
**
**  This program is under the terms of the BSD License.
*/

#include <callbacks.hpp>
#include <exceptions.hpp>

#ifdef TRITON_PYTHON_BINDINGS
  #include <pythonObjects.hpp>
  #include <pythonUtils.hpp>
  #include <pythonXFunctions.hpp>
#endif

namespace triton {
  namespace callbacks {

    Callbacks::Callbacks() {
      this->isDefined = false;
    }

    Callbacks::Callbacks(const Callbacks& copy) {
      #ifdef TRITON_PYTHON_BINDINGS
      this->pyUnmappedMemoryHitCallbacks        = copy.pyUnmappedMemoryHitCallbacks;
      this->pySymbolicSimplificationCallbacks   = copy.pySymbolicSimplificationCallbacks;
      #endif
      this->unmappedMemoryHitCallbacks          = copy.unmappedMemoryHitCallbacks;
      this->symbolicSimplificationCallbacks     = copy.symbolicSimplificationCallbacks;
      this->isDefined                           = copy.isDefined;
    }

    Callbacks::~Callbacks() {
    }

    void Callbacks::operator=(const Callbacks& copy) {
      #ifdef TRITON_PYTHON_BINDINGS
      this->pyUnmappedMemoryHitCallbacks        = copy.pyUnmappedMemoryHitCallbacks;
      this->pySymbolicSimplificationCallbacks   = copy.pySymbolicSimplificationCallbacks;
      #endif
      this->unmappedMemoryHitCallbacks          = copy.unmappedMemoryHitCallbacks;
      this->symbolicSimplificationCallbacks     = copy.symbolicSimplificationCallbacks;
      this->isDefined                           = copy.isDefined;
    }

    void Callbacks::addCallback(triton::callbacks::unmappedMemoryHitCallback cb) {
      this->unmappedMemoryHitCallbacks.push_back(cb);
      this->isDefined = true;
    }

    void Callbacks::addCallback(triton::callbacks::symbolicSimplificationCallback cb) {
      this->symbolicSimplificationCallbacks.push_back(cb);
      this->isDefined = true;
    }

    #ifdef TRITON_PYTHON_BINDINGS
    void Callbacks::addCallback(triton::callbacks::callback_e kind, PyObject* function) {
      switch (kind) {
        case UNMAPPED_MEMORY_HIT:
            this->pyUnmappedMemoryHitCallbacks.push_back(function);
          break;
        case SYMBOLIC_SIMPLIFICATION:
            this->pySymbolicSimplificationCallbacks.push_back(function);
          break;
        default:
          throw triton::exceptions::Callbacks("Callbacks::addCallback(): Invalid kind of callback.");
      };
      this->isDefined = true;
    }
    #endif

    void Callbacks::deleteCallback(triton::callbacks::unmappedMemoryHitCallback cb) {
      this->unmappedMemoryHitCallbacks.remove(cb);
      if (this->countCallbacks() == 0)
        this->isDefined = false;
    }

    void Callbacks::deleteCallback(triton::callbacks::symbolicSimplificationCallback cb) {
      this->symbolicSimplificationCallbacks.remove(cb);
      if (this->countCallbacks() == 0)
        this->isDefined = false;
    }

    #ifdef TRITON_PYTHON_BINDINGS
    void Callbacks::deleteCallback(triton::callbacks::callback_e kind, PyObject* function) {
      switch (kind) {
        case UNMAPPED_MEMORY_HIT:
            this->pyUnmappedMemoryHitCallbacks.remove(function);
          break;
        case SYMBOLIC_SIMPLIFICATION:
            this->pySymbolicSimplificationCallbacks.remove(function);
          break;
        default:
          throw triton::exceptions::Callbacks("Callbacks::deleteCallback(): Invalid kind of callback.");
      };

      if (this->countCallbacks() == 0)
        this->isDefined = false;
    }
    #endif

    triton::ast::AbstractNode* Callbacks::processCallbacks(triton::callbacks::callback_e kind, triton::ast::AbstractNode* node) const {
      switch (kind) {
        case triton::callbacks::SYMBOLIC_SIMPLIFICATION: {
          // C++ callbacks
          std::list<triton::callbacks::symbolicSimplificationCallback>::const_iterator it1;
          for (it1 = this->symbolicSimplificationCallbacks.begin(); it1 != this->symbolicSimplificationCallbacks.end(); it1++) {
            node = (*it1)(node);
            if (node == nullptr)
              throw triton::exceptions::Callbacks("Callbacks::processCallbacks(SYMBOLIC_SIMPLIFICATION): You cannot return a nullptr node.");
          }

          #ifdef TRITON_PYTHON_BINDINGS
          // Python callbacks
          std::list<PyObject*>::const_iterator it2;
          for (it2 = this->pySymbolicSimplificationCallbacks.begin(); it2 != this->pySymbolicSimplificationCallbacks.end(); it2++) {

            /* Create function args */
            PyObject* args = triton::bindings::python::xPyTuple_New(1);
            PyTuple_SetItem(args, 0, triton::bindings::python::PyAstNode(node));

            /* Call the callback */
            PyObject* ret = PyObject_CallObject(*it2, args);

            /* Check the call */
            if (ret == nullptr) {
              PyErr_Print();
              throw triton::exceptions::Callbacks("Callbacks::processCallbacks(SYMBOLIC_SIMPLIFICATION): Fail to call the python callback.");
            }

            /* Check if the callback has returned a AbstractNode */
            if (!PyAstNode_Check(ret))
              throw triton::exceptions::Callbacks("Callbacks::processCallbacks(SYMBOLIC_SIMPLIFICATION): You must return a AstNode object.");

            /* Update node */
            node = PyAstNode_AsAstNode(ret);
            Py_DECREF(args);
          }
          #endif
          break;
        }
        default:
          throw triton::exceptions::Callbacks("Callbacks::processCallbacks(): Invalid kind of callback for this C++ polymorphism.");
      };

      return node;
    }

    triton::uint8 Callbacks::processCallbacks(triton::callbacks::callback_e kind, triton::uint64 address) const {
      switch (kind) {
        case triton::callbacks::UNMAPPED_MEMORY_HIT: {
          triton::uint8 value = 0;

          // C++ callbacks
          std::list<triton::callbacks::unmappedMemoryHitCallback>::const_iterator it1;
          for (it1 = this->unmappedMemoryHitCallbacks.begin(); it1 != this->unmappedMemoryHitCallbacks.end(); it1++)
            value = (*it1)(address);

          #ifdef TRITON_PYTHON_BINDINGS
          // Python callbacks
          std::list<PyObject*>::const_iterator it2;
          for (it2 = this->pyUnmappedMemoryHitCallbacks.begin(); it2 != this->pyUnmappedMemoryHitCallbacks.end(); it2++) {

            /* Create function args */
            PyObject* args = triton::bindings::python::xPyTuple_New(1);
            PyTuple_SetItem(args, 0, triton::bindings::python::PyLong_FromUint64(address));

            /* Call the callback */
            PyObject* ret = PyObject_CallObject(*it2, args);

            /* Check the call */
            if (ret == nullptr) {
              PyErr_Print();
              throw triton::exceptions::Callbacks("Callbacks::processCallbacks(UNMAPPED_MEMORY_HIT): Fail to call the python callback.");
            }

            /* Check if the callback has returned an integer */
            if (!PyLong_Check(ret) && !PyInt_Check(ret))
              throw triton::exceptions::Callbacks("Callbacks::processCallbacks(UNMAPPED_MEMORY_HIT): You must return a Integer object.");

            value = static_cast<triton::uint8>(triton::bindings::python::PyLong_AsUint32(ret) & 0xff);
            Py_DECREF(args);
          }
          #endif
          return value;
        }
        default:
          throw triton::exceptions::Callbacks("Callbacks::processCallbacks(): Invalid kind of callback for this C++ polymorphism.");
      };
    }

    triton::uint32 Callbacks::countCallbacks(void) const {
      triton::uint32 count = 0;

      count += this->unmappedMemoryHitCallbacks.size();
      count += this->symbolicSimplificationCallbacks.size();
      #ifdef TRITON_PYTHON_BINDINGS
      count += this->pyUnmappedMemoryHitCallbacks.size();
      count += this->pySymbolicSimplificationCallbacks.size();
      #endif

      return count;
    }

  }; /* callbacks namespace */
}; /* triton namespace */
<|endoftext|>


<commit_before>/*
	This file is part of cpp-ethereum.

	cpp-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	cpp-ethereum is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file createRandomTest.cpp
 * @author Christoph Jentzsch <jentzsch.simulationsoftware@gmail.com>
 * @date 2014
 * Creating a random virtual machine test.
 */

#include <string>
#include <iostream>
#include <chrono>
#include <boost/random.hpp>
#include <boost/filesystem/path.hpp>
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <json_spirit/json_spirit.h>
#include <json_spirit/json_spirit_reader_template.h>
#include <json_spirit/json_spirit_writer_template.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/CommonData.h>
#include <libevmcore/Instruction.h>
#include <libevm/VMFactory.h>
#include "vm.h"

using namespace std;
using namespace json_spirit;
using namespace dev;

void doMyTests(json_spirit::mValue& v);

int main(int argc, char *argv[])
{
	g_logVerbosity = 0;

	// create random code

	boost::random::mt19937 gen;

	auto now = chrono::steady_clock::now().time_since_epoch();
	auto timeSinceEpoch = chrono::duration_cast<chrono::nanoseconds>(now).count();
	gen.seed(static_cast<unsigned int>(timeSinceEpoch));
	boost::random::uniform_int_distribution<> lengthOfCodeDist(2, 16);
	boost::random::uniform_int_distribution<> opcodeDist(0, 255);
	boost::random::uniform_int_distribution<> BlockInfoOpcodeDist(0x40, 0x45);
	boost::random::variate_generator<boost::mt19937&,
			boost::random::uniform_int_distribution<> > randGen(gen, opcodeDist);
	boost::random::variate_generator<boost::mt19937&,
			boost::random::uniform_int_distribution<> > randGenBlockInfoOpcode(gen, BlockInfoOpcodeDist);

	int lengthOfCode  = lengthOfCodeDist(gen);
	string randomCode;

	for (int i = 0; i < lengthOfCode; ++i)
	{
		if (i < 8 && (randGen() < 192))
		{
			randomCode += toHex(toCompactBigEndian((uint8_t)randGenBlockInfoOpcode()));
			continue;
		}

		uint8_t opcode = randGen();
		// disregard all invalid commands, except of one (0x0c)
		if ((dev::eth::isValidInstruction(dev::eth::Instruction(opcode)) || (opcode == 0x0c)))
			randomCode += toHex(toCompactBigEndian(opcode));
		else
			i--;
	}

	const string s =\
"{\n\
	\"randomVMtest\": {\n\
		\"env\" : {\n\
			\"previousHash\" : \"5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6\",\n\
			\"currentNumber\" : \"0\",\n\
			\"currentGasLimit\" : \"1000000\",\n\
			\"currentDifficulty\" : \"115792089237316195423570985008687907853269984665640564039457584007913129639935\",\n\
			\"currentTimestamp\" : 5,\n\
			\"currentCoinbase\" : \"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba\"\n\
		},\n\
		\"pre\" : {\n\
			\"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6\" : {\n\
				\"balance\" : \"1000000000000000000\",\n\
				\"nonce\" : 0,\n\
				\"code\" : \"random\",\n\
				\"storage\": {}\n\
			}\n\
		},\n\
		\"exec\" : {\n\
			\"address\" : \"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6\",\n\
			\"origin\" : \"cd1722f3947def4cf144679da39c4c32bdc35681\",\n\
			\"caller\" : \"cd1722f3947def4cf144679da39c4c32bdc35681\",\n\
			\"value\" : \"1000000000000000000\",\n\
			\"data\" : \"\",\n\
			\"gasPrice\" : \"100000000000000\",\n\
			\"gas\" : \"10000\"\n\
		}\n\
	}\n\
}";

	mValue v;
	read_string(s, v);

	// insert new random code
	v.get_obj().find("randomVMtest")->second.get_obj().find("pre")->second.get_obj().begin()->second.get_obj()["code"] = "0x" + randomCode + "55";

	// execute code in vm
	doMyTests(v);

	// stream to output for further handling by the bash script
	cout << json_spirit::write_string(v, true);

	return 0;
}

void doMyTests(json_spirit::mValue& v)
{
	for (auto& i: v.get_obj())
	{
		cnote << i.first;
		mObject& o = i.second.get_obj();

		assert(o.count("env") > 0);
		assert(o.count("pre") > 0);
		assert(o.count("exec") > 0);

		dev::test::FakeExtVM fev;
		fev.importEnv(o["env"].get_obj());
		fev.importState(o["pre"].get_obj());

		o["pre"] = mValue(fev.exportState());

		fev.importExec(o["exec"].get_obj());
		if (fev.code.empty())
		{
			fev.thisTxCode = get<3>(fev.addresses.at(fev.myAddress));
			fev.code = fev.thisTxCode;
		}

		bytes output;
		auto vm = eth::VMFactory::create(fev.gas);

		u256 gas;
		bool vmExceptionOccured = false;
		try
		{
			output = vm->go(fev, fev.simpleTrace()).toBytes();
			gas = vm->gas();
		}
		catch (eth::VMException const& _e)
		{
			cnote << "VM did throw an exception: " << diagnostic_information(_e);
			vmExceptionOccured = true;
		}
		catch (Exception const& _e)
		{
			cnote << "VM did throw an exception: " << diagnostic_information(_e);
		}
		catch (std::exception const& _e)
		{
			cnote << "VM did throw an exception: " << _e.what();
		}

		// delete null entries in storage for the sake of comparison

		for (auto  &a: fev.addresses)
		{
			vector<u256> keystoDelete;
			for (auto &s: get<2>(a.second))
			{
				if (s.second == 0)
					keystoDelete.push_back(s.first);
			}
			for (auto const key: keystoDelete )
			{
				get<2>(a.second).erase(key);
			}
		}

		o["env"] = mValue(fev.exportEnv());
		o["exec"] = mValue(fev.exportExec());
		if (!vmExceptionOccured)
		{
			o["post"] = mValue(fev.exportState());
			o["callcreates"] = fev.exportCallCreates();
			o["out"] = "0x" + toHex(output);
			fev.push(o, "gas", gas);
			o["logs"] = test::exportLog(fev.sub.logs);
		}
	}
}
<commit_msg>random test optimization<commit_after>/*
	This file is part of cpp-ethereum.

	cpp-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	cpp-ethereum is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file createRandomTest.cpp
 * @author Christoph Jentzsch <jentzsch.simulationsoftware@gmail.com>
 * @date 2014
 * Creating a random virtual machine test.
 */

#include <string>
#include <iostream>
#include <chrono>
#include <boost/random.hpp>
#include <boost/filesystem/path.hpp>
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <json_spirit/json_spirit.h>
#include <json_spirit/json_spirit_reader_template.h>
#include <json_spirit/json_spirit_writer_template.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/CommonData.h>
#include <libevmcore/Instruction.h>
#include <libevm/VMFactory.h>
#include "vm.h"

using namespace std;
using namespace json_spirit;
using namespace dev;

void doMyTests(json_spirit::mValue& v);

int main(int argc, char *argv[])
{
	g_logVerbosity = 0;

	// create random code

	boost::random::mt19937 gen;

	auto now = chrono::steady_clock::now().time_since_epoch();
	auto timeSinceEpoch = chrono::duration_cast<chrono::nanoseconds>(now).count();
	gen.seed(static_cast<unsigned int>(timeSinceEpoch));
	boost::random::uniform_int_distribution<> lengthOfCodeDist(2, 16);
	boost::random::uniform_int_distribution<> opcodeDist(0, 255);
	boost::random::uniform_int_distribution<> BlockInfoOpcodeDist(0x40, 0x45);
	boost::random::variate_generator<boost::mt19937&,
			boost::random::uniform_int_distribution<> > randGen(gen, opcodeDist);
	boost::random::variate_generator<boost::mt19937&,
			boost::random::uniform_int_distribution<> > randGenBlockInfoOpcode(gen, BlockInfoOpcodeDist);

	int lengthOfCode  = lengthOfCodeDist(gen);
	string randomCode;

	for (int i = 0; i < lengthOfCode; ++i)
	{
		if (i < 8 && (randGen() < 192))
		{
			randomCode += toHex(toCompactBigEndian((uint8_t)randGenBlockInfoOpcode()));
			continue;
		}

		uint8_t opcode = randGen();
		// disregard all invalid commands, except of one (0x0c)
		if ((dev::eth::isValidInstruction(dev::eth::Instruction(opcode)) || (opcode == 0x0c)))
			randomCode += toHex(toCompactBigEndian(opcode));
		else
			i--;
	}

	const string s =\
"{\n\
	\"randomVMtest\": {\n\
		\"env\" : {\n\
			\"previousHash\" : \"5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6\",\n\
			\"currentNumber\" : \"0\",\n\
			\"currentGasLimit\" : \"1000000\",\n\
			\"currentDifficulty\" : \"115792089237316195423570985008687907853269984665640564039457584007913129639935\",\n\
			\"currentTimestamp\" : 2,\n\
			\"currentCoinbase\" : \"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba\"\n\
		},\n\
		\"pre\" : {\n\
			\"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6\" : {\n\
				\"balance\" : \"1000000000000000000\",\n\
				\"nonce\" : 0,\n\
				\"code\" : \"random\",\n\
				\"storage\": {}\n\
			}\n\
		},\n\
		\"exec\" : {\n\
			\"address\" : \"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6\",\n\
			\"origin\" : \"cd1722f3947def4cf144679da39c4c32bdc35681\",\n\
			\"caller\" : \"cd1722f3947def4cf144679da39c4c32bdc35681\",\n\
			\"value\" : \"1000000000000000000\",\n\
			\"data\" : \"\",\n\
			\"gasPrice\" : \"100000000000000\",\n\
			\"gas\" : \"10000\"\n\
		}\n\
	}\n\
}";

	mValue v;
	read_string(s, v);

	// insert new random code
	v.get_obj().find("randomVMtest")->second.get_obj().find("pre")->second.get_obj().begin()->second.get_obj()["code"] = "0x" + randomCode + "55";

	// execute code in vm
	doMyTests(v);

	// stream to output for further handling by the bash script
	cout << json_spirit::write_string(v, true);

	return 0;
}

void doMyTests(json_spirit::mValue& v)
{
	for (auto& i: v.get_obj())
	{
		cnote << i.first;
		mObject& o = i.second.get_obj();

		assert(o.count("env") > 0);
		assert(o.count("pre") > 0);
		assert(o.count("exec") > 0);

		dev::test::FakeExtVM fev;
		fev.importEnv(o["env"].get_obj());
		fev.importState(o["pre"].get_obj());

		o["pre"] = mValue(fev.exportState());

		fev.importExec(o["exec"].get_obj());
		if (fev.code.empty())
		{
			fev.thisTxCode = get<3>(fev.addresses.at(fev.myAddress));
			fev.code = fev.thisTxCode;
		}

		bytes output;
		auto vm = eth::VMFactory::create(fev.gas);

		u256 gas;
		bool vmExceptionOccured = false;
		try
		{
			output = vm->go(fev, fev.simpleTrace()).toBytes();
			gas = vm->gas();
		}
		catch (eth::VMException const& _e)
		{
			cnote << "VM did throw an exception: " << diagnostic_information(_e);
			vmExceptionOccured = true;
		}
		catch (Exception const& _e)
		{
			cnote << "VM did throw an exception: " << diagnostic_information(_e);
		}
		catch (std::exception const& _e)
		{
			cnote << "VM did throw an exception: " << _e.what();
		}

		// delete null entries in storage for the sake of comparison

		for (auto  &a: fev.addresses)
		{
			vector<u256> keystoDelete;
			for (auto &s: get<2>(a.second))
			{
				if (s.second == 0)
					keystoDelete.push_back(s.first);
			}
			for (auto const key: keystoDelete )
			{
				get<2>(a.second).erase(key);
			}
		}

		o["env"] = mValue(fev.exportEnv());
		o["exec"] = mValue(fev.exportExec());
		if (!vmExceptionOccured)
		{
			o["post"] = mValue(fev.exportState());
			o["callcreates"] = fev.exportCallCreates();
			o["out"] = "0x" + toHex(output);
			fev.push(o, "gas", gas);
			o["logs"] = test::exportLog(fev.sub.logs);
		}
	}
}
<|endoftext|>


<commit_before><commit_msg>Need "inStack[w]=false;" after line 65<commit_after><|endoftext|>