Logo Search packages:      
Sourcecode: nmap version File versions

nse_main.cc

#include "nse_main.h"

#include "nse_init.h"
#include "nse_nsock.h"
#include "nse_nmaplib.h"
#include "nse_debug.h"
#include "nse_macros.h"
#include "nse_string.h"

#include "nmap.h"
#include "nmap_error.h"
#include "portlist.h"
#include "nsock.h"
#include "NmapOps.h"
#include "timing.h"
#include "Target.h"
#include "nmap_tty.h"

extern NmapOps o;

struct run_record {
      short type; // 0 - hostrule; 1 - portrule
      unsigned int index; // index in the corresponding table
      Port* port;
      Target* host;
};

struct thread_record {
      lua_State* thread;
      int resume_arguments;
      unsigned int registry_idx; // index in the main state registry
      double runlevel;
      run_record* rr;
};

int current_hosts = 0;
int errfunc = 0;
std::list<std::list<struct thread_record> > torun_scripts;
std::list<struct thread_record> running_scripts;
std::list<struct thread_record> waiting_scripts;

class CompareRunlevels {
public:
      bool operator() (const struct thread_record& lhs, const struct thread_record& rhs) {
            return lhs.runlevel < rhs.runlevel;
      }
};

// prior execution
int process_preparerunlevels(std::list<struct thread_record> torun_threads);
int process_preparehost(lua_State* L, Target* target, std::list<struct thread_record>& torun_threads);
int process_preparethread(lua_State* L, struct run_record rr, struct thread_record* tr);

// helper functions
int process_getScriptId(lua_State* L, struct script_scan_result* ssr);
int process_pickScriptsForPort(
            lua_State* L, 
            Target* target, 
            Port* port,
            std::vector<run_record>& torun);

// execution
int process_mainloop(lua_State* L);
int process_waiting2running(lua_State* L, int resume_arguments);
int process_finalize(lua_State* L, unsigned int registry_idx);

// post execution
int cleanup_threads(std::list<struct thread_record> trs);

static int panic (lua_State *L)
{
  const char *err = lua_tostring(L, 1);
  fatal("Unprotected error in Lua:\n%s\n", err);
  return 0;
}

int script_updatedb (void)
{
  int status;
  int ret = SCRIPT_ENGINE_SUCCESS;
  lua_State *L;

  SCRIPT_ENGINE_VERBOSE(
      log_write(LOG_STDOUT, "%s: Updating rule database.\n", 
        SCRIPT_ENGINE);
      )

  L = luaL_newstate();
  if (L == NULL)
  {
    error("%s: Failed luaL_newstate()", SCRIPT_ENGINE);
    return 0;
  }
  lua_atpanic(L, panic);
  
  status = lua_cpcall(L, init_lua, NULL);
  if (status != 0)
  {
    error("%s: error while initializing Lua State:\n%s\n",
        SCRIPT_ENGINE, lua_tostring(L, -1));
    ret = SCRIPT_ENGINE_ERROR;
    goto finishup;
  }
  
  lua_settop(L, 0); // safety, is 0 anyway
  lua_rawgeti(L, LUA_REGISTRYINDEX, errfunc); // index 1

  lua_pushcclosure(L, init_updatedb, 0);
  status = lua_pcall(L, 0, 0, 1);
  if(status != 0)
  {
    error("%s: error while updating Script Database:\n%s\n",
        SCRIPT_ENGINE, lua_tostring(L, -1));
    ret = SCRIPT_ENGINE_ERROR;
    goto finishup;
  }
  
  log_write(LOG_STDOUT, "NSE script database updated successfully.\n");
  
  finishup:
    lua_close(L);
    if (ret != SCRIPT_ENGINE_SUCCESS)
    {
      error("%s: Aborting database update.\n", SCRIPT_ENGINE);
      return SCRIPT_ENGINE_ERROR;
    }
    else
      return SCRIPT_ENGINE_SUCCESS;
}

/* check the script-arguments provided to nmap (--script-args) before
 * scanning starts - otherwise the whole scan will run through and be
 * aborted before script-scanning 
 */
int script_check_args (void)
{
  int ret = SCRIPT_ENGINE_SUCCESS, status;
  lua_State* L = luaL_newstate();

  if (L == NULL)
    fatal("Error opening lua, for checking arguments\n");
  lua_atpanic(L, panic);

  /* set all global libraries (we'll need the string-lib) */
  status = lua_cpcall(L, init_lua, NULL);
  if (status != 0)
  {
    error("%s: error while initializing Lua State:\n%s\n",
        SCRIPT_ENGINE, lua_tostring(L, -1));
    ret = SCRIPT_ENGINE_ERROR;
    goto finishup;
  }

  lua_pushcclosure(L, init_parseargs, 0);
  lua_pushstring(L, o.scriptargs);
  lua_pcall(L, 1, 1, 0);

  if (!lua_isfunction(L, -1))
    ret = SCRIPT_ENGINE_ERROR;

  finishup:
      lua_close(L);
      return ret;
}

/* open a lua instance
 * open the lua standard libraries
 * open all the scripts and prepare them for execution
 *    (export nmap bindings, add them to host/port rulesets etc.)
 * apply all scripts on all hosts
 * */
int script_scan(std::vector<Target*> &targets) {
      int status;
      std::vector<Target*>::iterator target_iter;
      std::list<std::list<struct thread_record> >::iterator runlevel_iter;
      std::list<struct thread_record>::iterator thr_iter;
      std::list<struct thread_record> torun_threads;
    std::vector<std::string>::iterator script_iter;
      lua_State* L;

      o.current_scantype = SCRIPT_SCAN;

      SCRIPT_ENGINE_VERBOSE(
                  log_write(LOG_STDOUT, "%s: Initiating script scanning.\n", SCRIPT_ENGINE);
                  )

      SCRIPT_ENGINE_DEBUGGING(
            unsigned int tlen = targets.size();
            char targetstr[128];
            if(tlen > 1)
                  log_write(LOG_STDOUT, "%s: Script scanning %d hosts.\n", 
                        SCRIPT_ENGINE, tlen);
            else
                  log_write(LOG_STDOUT, "%s: Script scanning %s.\n", 
                        SCRIPT_ENGINE, (*targets.begin())->NameIP(targetstr, sizeof(targetstr)));
      )

      L = luaL_newstate();
      if(L == NULL) {
            error("%s: Failed luaL_newstate()", SCRIPT_ENGINE);
        return SCRIPT_ENGINE_ERROR;
      }
    lua_atpanic(L, panic);

    status = lua_cpcall(L, init_lua, NULL);
    if (status != 0)
    {
      error("%s: error while initializing Lua State:\n%s\n",
          SCRIPT_ENGINE, lua_tostring(L, -1));
      status = SCRIPT_ENGINE_ERROR;
      goto finishup;
    }

      //set the arguments - if provided
    status = lua_cpcall(L, init_setargs, NULL);
    if (status != 0)
    {
      error("%s: error while setting arguments for scripts:\n%s\n",
          SCRIPT_ENGINE, lua_tostring(L, -1));
      status = SCRIPT_ENGINE_ERROR;
      goto finishup;
    }

    lua_settop(L, 0); // safety, is 0 anyway
    lua_rawgeti(L, LUA_REGISTRYINDEX, errfunc); // index 1

    if (!lua_checkstack(L, o.chosenScripts.size() + 1))
    {
      error("%s: stack overflow at %s:%d", SCRIPT_ENGINE, __FILE__, __LINE__);
      status = SCRIPT_ENGINE_ERROR;
      goto finishup;
    }
    lua_pushcclosure(L, init_rules, 0);
    for (script_iter = o.chosenScripts.begin();
         script_iter != o.chosenScripts.end();
         script_iter++)
      lua_pushstring(L, script_iter->c_str());
    status = lua_pcall(L, o.chosenScripts.size(), 0, 1);
    if (status != 0)
    {
      error("%s: error while initializing script rules:\n%s\n",
          SCRIPT_ENGINE, lua_tostring(L, -1));
      status = SCRIPT_ENGINE_ERROR;
      goto finishup;
    }

      SCRIPT_ENGINE_DEBUGGING(log_write(LOG_STDOUT, "%s: Matching rules.\n", SCRIPT_ENGINE);)

      for(target_iter = targets.begin(); target_iter != targets.end(); target_iter++) {
            std::string key = ((Target*) (*target_iter))->targetipstr();
        lua_rawgeti(L, LUA_REGISTRYINDEX, current_hosts);
        lua_pushstring(L, key.c_str());
        lua_pushlightuserdata(L, (void *) *target_iter);
        lua_settable(L, -3);
        lua_pop(L, 1);

            status = process_preparehost(L, *target_iter, torun_threads);
            if(status != SCRIPT_ENGINE_SUCCESS){
                  goto finishup;
            }
      }

      status = process_preparerunlevels(torun_threads);
      if(status != SCRIPT_ENGINE_SUCCESS) {
            goto finishup;
      }

      SCRIPT_ENGINE_DEBUGGING(log_write(LOG_STDOUT, "%s: Running scripts.\n", SCRIPT_ENGINE);)
      
      for(runlevel_iter = torun_scripts.begin(); runlevel_iter != torun_scripts.end(); runlevel_iter++) {
            running_scripts = (*runlevel_iter);

            SCRIPT_ENGINE_DEBUGGING(log_write(LOG_STDOUT, "%s: Runlevel: %f\n", 
                              SCRIPT_ENGINE,
                              running_scripts.front().runlevel);)

            /* Start the time-out clocks for targets with scripts in this
             * runlevel.  The clock is stopped in process_finalize().
             */
            for (thr_iter = running_scripts.begin();
                 thr_iter != running_scripts.end();
                 thr_iter++)
                  if (!thr_iter->rr->host->timeOutClockRunning())
                        thr_iter->rr->host->startTimeOutClock(NULL);

            status = process_mainloop(L);
            if(status != SCRIPT_ENGINE_SUCCESS){
                  goto finishup;
            }
      }
      

finishup:
      SCRIPT_ENGINE_DEBUGGING(
                  log_write(LOG_STDOUT, "%s: Script scanning completed.\n", SCRIPT_ENGINE);
                  )
      lua_close(L);
      cleanup_threads(torun_threads);
      torun_scripts.clear();
      if(status != SCRIPT_ENGINE_SUCCESS) {
            error("%s: Aborting script scan.", SCRIPT_ENGINE);
            return SCRIPT_ENGINE_ERROR;
      } else {
            return SCRIPT_ENGINE_SUCCESS;
      }
}

int process_mainloop(lua_State *L) {
      int state;
      int unfinished = running_scripts.size() + waiting_scripts.size(); 
      struct script_scan_result ssr;
      struct thread_record current;
      ScanProgressMeter progress = ScanProgressMeter(SCRIPT_ENGINE);

      double total = (double) unfinished;
      double done = 0;

      std::list<struct thread_record>::iterator iter;
      struct timeval now;

      // while there are scripts in running or waiting state, we loop.
      // we rely on nsock_loop to protect us from busy loops when 
      // all scripts are waiting.
      while( unfinished > 0 ) {

            if(l_nsock_loop(50) == NSOCK_LOOP_ERROR) {
                  error("%s: An error occured in the nsock loop", SCRIPT_ENGINE);
                  return SCRIPT_ENGINE_ERROR;
            }

            unfinished = running_scripts.size() + waiting_scripts.size();

            if (keyWasPressed()) {
                  done = 1.0 - (((double) unfinished) / total);
                  if (o.verbose > 1 || o.debugging) {
                        log_write(LOG_STDOUT, "Active NSE scripts: %d\n", unfinished);
                        log_flush(LOG_STDOUT);
                  }
                  progress.printStats(done, NULL);
            }

            SCRIPT_ENGINE_VERBOSE(
                  if(progress.mayBePrinted(NULL)) { 
                        done = 1.0 - (((double) unfinished) / total);
                        if(o.verbose > 1 || o.debugging)
                              progress.printStats(done, NULL);
                        else
                              progress.printStatsIfNeccessary(done, NULL);
                  })

            gettimeofday(&now, NULL);

            for(iter = waiting_scripts.begin(); iter != waiting_scripts.end(); iter++)
                  if (iter->rr->host->timedOut(&now)) {
                        running_scripts.push_front((*iter));
                        waiting_scripts.erase(iter);
                        iter = waiting_scripts.begin();
                  }


        while (!running_scripts.empty()) {
                  current = *(running_scripts.begin());

                  if (current.rr->host->timedOut(&now))
                        state = LUA_ERRRUN;
                  else
                        state = lua_resume(current.thread, current.resume_arguments);
      
                  if(state == LUA_YIELD) {
                        // this script has performed a network io operation
                        // we put it in the waiting
                        // when the network io operation has completed,
                        // a callback from the nsock library will put the
                        // script back into the running state
                        
                        waiting_scripts.push_back(current);
                        running_scripts.pop_front();
                  } else if( state == 0) {
                        // this script has finished
                        // we first check if it produced output
                        // then we release the thread and remove it from the
                        // running_scripts list
      
                        if(lua_isstring (current.thread, -1)) {
                              SCRIPT_ENGINE_TRY(process_getScriptId(current.thread, &ssr));
                              ssr.output = nse_printable
                                    (lua_tostring(current.thread, -1), lua_objlen(current.thread, -1));
                              if(current.rr->type == 0) {
                                    current.rr->host->scriptResults.push_back(ssr);
                              } else if(current.rr->type == 1) {
                                    current.rr->port->scriptResults.push_back(ssr);
                                    current.rr->host->ports.numscriptresults++;
                              }
                              lua_pop(current.thread, 2);
                        }
      
                        SCRIPT_ENGINE_TRY(process_finalize(L, current.registry_idx));
                        SCRIPT_ENGINE_TRY(lua_gc(L, LUA_GCCOLLECT, 0));
                  } else {
                        // this script returned because of an error
                        // print the failing reason if the verbose level is high enough   
                        SCRIPT_ENGINE_DEBUGGING(
                              const char* errmsg = lua_tostring(current.thread, -1);
                              log_write(LOG_STDOUT, "%s: %s\n", SCRIPT_ENGINE, errmsg);
                        )
                        SCRIPT_ENGINE_TRY(process_finalize(L, current.registry_idx));
                  }
            } // while
      }

      progress.endTask(NULL, NULL);

      return SCRIPT_ENGINE_SUCCESS;
}

// If the target still has scripts in either running_scripts
// or waiting_scripts then it is still running.  This only
// pertains to scripts in the current runlevel.

int has_target_finished(Target *target) {
      std::list<struct thread_record>::iterator iter;

      for (iter = waiting_scripts.begin(); iter != waiting_scripts.end(); iter++)
            if (target == iter->rr->host) return 0;

      for (iter = running_scripts.begin(); iter != running_scripts.end(); iter++)
            if (target == iter->rr->host) return 0;

      return 1;
}

int process_finalize(lua_State* L, unsigned int registry_idx) {
      luaL_unref(L, LUA_REGISTRYINDEX, registry_idx);
      struct thread_record thr = running_scripts.front();

      running_scripts.pop_front();

      if (has_target_finished(thr.rr->host))
            thr.rr->host->stopTimeOutClock(NULL);

      return SCRIPT_ENGINE_SUCCESS;
}

int process_waiting2running(lua_State* L, int resume_arguments) {
      std::list<struct thread_record>::iterator iter;

      // find the lua state which has received i/o
      for(  iter = waiting_scripts.begin(); 
            (*iter).thread != L;
            iter++) {

            // It is very unlikely that a thread which
            // is not in the waiting queue tries to
            // continue
            // it does happen when they try to do socket i/o
            // inside a pcall

            // This also happens when we timeout a script
            // In this case, the script is still in the waiting
            // queue and we will have manually removed it from
            // the waiting queue so we just return.

            if(iter == waiting_scripts.end())
                  return SCRIPT_ENGINE_SUCCESS;
      }

      (*iter).resume_arguments = resume_arguments;

      // put the thread back into the running
      // queue
      //running_scripts.push_front((*iter));
      running_scripts.push_back((*iter));
      waiting_scripts.erase(iter);

      return SCRIPT_ENGINE_SUCCESS;
}

/* Tries to get the script id and store it in the script scan result structure
 * if no 'id' field is found, the filename field is used which we set in the 
 * setup phase. If someone changed the filename field to a nonstring we complain
 * */
int process_getScriptId(lua_State* L, struct script_scan_result *ssr) {

      lua_getfield(L, -2, "id");
      lua_getfield(L, -3, "filename");

      if(lua_isstring(L, -2)) {
            ssr->id = strdup(lua_tostring (L, -2));
      } else if(lua_isstring(L, -1)) {
            ssr->id = strdup(lua_tostring (L, -1));
      } else {
            error("%s: The script has no 'id' entry, the 'filename' entry was changed to:",
                  SCRIPT_ENGINE);
            l_dumpValue(L, -1);
            return SCRIPT_ENGINE_ERROR;
      }

      lua_pop(L, 2);

      return SCRIPT_ENGINE_SUCCESS;
}

/* try all host and all port rules against the 
 * state of the current target
 * make a list with run records for the scripts
 * which want to run
 * process all scripts in the list
 * */
int process_preparehost(lua_State* L, Target* target, std::list<struct thread_record>& torun_threads) {
      PortList* plist = &(target->ports);
      Port* current = NULL;
      size_t rules_count;
      unsigned int i;
      std::vector<run_record> torun;
      std::vector<run_record>::iterator iter;
      struct run_record rr;

      /* find the matching hostrules
       * */
      lua_getglobal(L, HOSTTESTS);
      rules_count = lua_objlen(L, -1);

      for(i = 1; i <= rules_count; i++) {
            lua_rawgeti(L, -1, i);

            lua_getfield(L, -1, "hostrule");

            lua_newtable(L);
            set_hostinfo(L, target);

            SCRIPT_ENGINE_LUA_TRY(lua_pcall(L, 1, 1, 0));

            if(lua_isboolean (L, -1) && lua_toboolean(L, -1)) {
                  rr.type = 0;
                  rr.index = i;
                  rr.port = NULL;
                  rr.host = target;
                  torun.push_back(rr);

                  SCRIPT_ENGINE_DEBUGGING(
                        lua_getfield(L, -2, "filename");
                        log_write(LOG_STDOUT, "%s: Will run %s against %s\n",
                              SCRIPT_ENGINE,
                              lua_tostring(L, -1),
                              target->targetipstr());
                        lua_pop(L, 1);
                  )
            }
            lua_pop(L, 2);
      }

      /* find the matching port rules
       * */
      lua_getglobal(L, PORTTESTS);

      /* we only publish hostinfo once per portrule */
      lua_newtable(L);
      set_hostinfo(L, target);

      /* because of the port iteration API we need to awkwardly iterate
       * over the kinds of ports we're interested in explictely.
       * */
      current = NULL;
      while((current = plist->nextPort(current, TCPANDUDP, PORT_OPEN)) != NULL) {
            SCRIPT_ENGINE_TRY(process_pickScriptsForPort(L, target, current, torun));
      }

      while((current = plist->nextPort(current, TCPANDUDP, PORT_OPENFILTERED)) != NULL) {
            SCRIPT_ENGINE_TRY(process_pickScriptsForPort(L, target, current, torun));
      }

      while((current = plist->nextPort(current, TCPANDUDP, PORT_UNFILTERED)) != NULL) {
            SCRIPT_ENGINE_TRY(process_pickScriptsForPort(L, target, current, torun));
      }

      // pop the hostinfo, we don't need it anymore
      lua_pop(L, 1);

      /* ok, let's setup threads for the scripts which said they'd like
       * to run 
       * Remember:
       * we have the hosttestset and the porttestset on the stack!
       * */
      struct thread_record tr;

      for(iter = torun.begin(); iter != torun.end(); iter++) {
            /* If it is a host rule, execute the action
             * and append the output to the host output i
             * If it is a port rule, append the output to
             * the port and increase the number of scripts
             * which produced output. We need that number
             * to generate beautiful output later.
             * */
            switch((*iter).type) {
                  case 0: // this script runs against a host
                        lua_pushvalue(L, -2);
                        SCRIPT_ENGINE_TRY(process_preparethread(L, (*iter), &tr));
                        lua_pop(L, 1);
                        break;
                  case 1: // this script runs against a port
                        lua_pushvalue(L, -1);
                        SCRIPT_ENGINE_TRY(process_preparethread(L, (*iter), &tr));
                        lua_pop(L, 1);
                        break;
                  default:
                        fatal("%s: In: %s:%i This should never happen.", 
                                    SCRIPT_ENGINE, __FILE__, __LINE__);
            }

            torun_threads.push_back(tr);
      }
      lua_pop(L, 2);

      torun.clear();
      return SCRIPT_ENGINE_SUCCESS;
}

int process_preparerunlevels(std::list<struct thread_record> torun_threads) {
      std::list<struct thread_record> current_runlevel;
      std::list<struct thread_record>::iterator runlevel_iter;
      double runlevel_idx = 0.0;
      
      torun_threads.sort(CompareRunlevels());

      for(  runlevel_iter = torun_threads.begin(); 
            runlevel_iter != torun_threads.end(); 
            runlevel_iter++) {

            if(runlevel_idx < (*runlevel_iter).runlevel) {
                  runlevel_idx = (*runlevel_iter).runlevel;
                  current_runlevel.clear();
                  //push_back an empty list in which we store all scripts of the 
                  //current runlevel...
                  torun_scripts.push_back(current_runlevel);
            }

            torun_scripts.back().push_back(*runlevel_iter);
      }

      return SCRIPT_ENGINE_SUCCESS;
}

/* Because we can't iterate over all ports of interest in one go
 * we need to do port matching in a separate function (unlike host
 * rule matching)
 * Note that we assume that at -2 on the stack we can find the portrules
 * and at -1 the hostinfo table
 * */
int process_pickScriptsForPort(
            lua_State* L, 
            Target* target, 
            Port* port,
            std::vector<run_record>& torun) {
      size_t rules_count = lua_objlen(L, -2);
      struct run_record rr;
      unsigned int i;

      for(i = 1; i <= rules_count; i++) {
            lua_rawgeti(L, -2, i);

            lua_getfield(L, -1, PORTRULE);

            lua_pushvalue(L, -3);

            lua_newtable(L);
            set_portinfo(L, port);

            SCRIPT_ENGINE_LUA_TRY(lua_pcall(L, 2, 1, 0));

            if(lua_isboolean (L, -1) && lua_toboolean(L, -1)) {
                  rr.type = 1;
                  rr.index = i;
                  rr.port = port;
                  rr.host = target;
                  torun.push_back(rr);

                  SCRIPT_ENGINE_DEBUGGING(
                              lua_getfield(L, -2, "filename");
                              log_write(LOG_STDOUT, "%s: Will run %s against %s:%d\n",
                                    SCRIPT_ENGINE,
                                    lua_tostring(L, -1),
                                    target->targetipstr(),
                                    port->portno);
                              lua_pop(L, 1);
                              )
            } else if(!lua_isboolean (L, -1)) {
                  lua_getfield(L, -2, "filename");
                  error("%s: Rule in %s returned %s but boolean was expected.",
                              SCRIPT_ENGINE,
                              lua_tostring(L, -1),
                              lua_typename(L, lua_type(L, -2)));
                  return SCRIPT_ENGINE_LUA_ERROR;
            }
            lua_pop(L, 2);
      }

      return SCRIPT_ENGINE_SUCCESS;
}

/* Create a new lua thread and prepare it for execution
 * we store target info in the thread so that the mainloop
 * knows where to put the script result
 * */
int process_preparethread(lua_State* L, struct run_record rr, struct thread_record* tr){

      lua_State *thread = lua_newthread(L);

      lua_rawgeti(L, -2, rr.index); // get the script closure

      // move the script closure into the thread
      lua_xmove(L, thread, 1); 

      // store the target of this thread in the thread
      struct run_record *rr_thread = (struct run_record*) safe_malloc(sizeof(struct run_record));
      rr_thread->type = rr.type;
      rr_thread->index = rr.index;
      rr_thread->host = rr.host;
      rr_thread->port = rr.port;

      
      lua_getfield(thread, -1, RUNLEVEL);
      tr->runlevel = lua_tonumber(thread, -1);
      lua_pop(thread, 1);

      // prepare the thread for a resume by
      // pushing the action method onto the stack
      lua_getfield(thread, -1, ACTION);

      // make the info table
      lua_newtable(thread); 
      set_hostinfo(thread, rr.host);

      tr->thread = thread;
      tr->rr = rr_thread;
      tr->resume_arguments = 1;

      // we store the thread in the registry to prevent
      // garbage collection +
      tr->registry_idx = luaL_ref(L, LUA_REGISTRYINDEX);

      /* if this is a host rule we don't have
       * a port state
       * */
      if(rr.port != NULL) {
            lua_newtable(thread);
            set_portinfo(thread, rr.port);
            tr->resume_arguments = 2;
      }

      return SCRIPT_ENGINE_SUCCESS;
}

int cleanup_threads(std::list<struct thread_record> trs)
{
      std::list<struct thread_record>::iterator triter;

      for (triter = trs.begin(); triter != trs.end(); triter++)
            free((*triter).rr);

      return SCRIPT_ENGINE_SUCCESS;
}


Generated by  Doxygen 1.6.0   Back to index