def dispatch(newsock, rroot, aroot, tcfg): global threadcount; global threadhigh # We may have threads available, or we may not. Having threads # available is the simple case, so we handle that first. if threadcount < tcfg.max: # We increment the thread counter immediately for the # best load limiting; otherwise we are the mercy of # whenever the scheduling process gets the new thread far # enough along to increment the count. ruleslock.acquire() threadcount += 1 if threadcount > threadhigh: threadhigh = threadcount ruleslock.release() thread.start_new_thread(threadrule, (newsock, rroot, aroot)) return # Either threads are off entirely or we are over the thread limit. # If we've hit the limit, what we do depends on whether maxclass is # set; if it is, instead of evaluating the rules in the mainline we # synthetically produce a match against that class. # We respect maxclass if and only if threading is enabled at all; # otherwise we always evaluate in the mainline as a single-threaded # program. if tcfg.maxclass and tcfg.max: # Unfortunately we need to duplicate a bit of rule()'s work, # as we need a hostinfo object as well as the fake rule. hi = hinfo.fromfd(newsock) if not hi: log.debug(1, "could not get hostinfo in threadmax") proc.closesock(newsock) return log.debug(2, "too many threads, putting %s connection in %s" % \ (hi.getip(), tcfg.maxclass)) # We have to add the GLOBAL (fake) rule in order to follow # the rules; this could be important if the thread overflow # class runs something. res = [newsock, hi, [rules.genfakerule(tcfg.maxclass), rules.globalrule]] else: if tcfg.max: log.debug(1, "too many threads, handling new socket directly") res = rule(newsock, rroot, aroot) if not res: return action(res[0], res[1], res[2], aroot)
def rule(newsock, rroot, aroot): global totconnects; global totrules; global totruleTime hi = hinfo.fromfd(newsock) if not hi: log.debug(1, "Could not get hostinfo, passing.") proc.closesock(newsock) return None # At this point this is a real connection and we will count it. ruleslock.acquire() totconnects += 1 ruleslock.release() # If we are missing one or the other root, there is # no point in doing anything; we can never match an # action. Kill it off and punt. if not (rroot and aroot): log.debug(2, "A root is missing or empty, dropping %s" %\ (conninfo(hi),)) proc.closesock(newsock) return None # Run it past the rules, and see if anything comes out. If not # we're done. ruleslock.acquire(); totrules += 1; ruleslock.release() st = time.time() rmatch = rroot.eval(hi) et = time.time() ruleslock.acquire(); totruleTime += (et-st); ruleslock.release() if not rmatch: log.debug(2, "Nothing matched %s" % (conninfo(hi),)) proc.closesock(newsock) return None return (newsock, hi, rmatch)
return rmnames = [x.clsname for x in rmatch] # Run it past the actions and see if we have an action to do. # If not, we're done. Action evaluation itself can fail if the # configuration file supplies a bad string that cannot be formatted. action = None try: action = aroot.genaction(hi, rmatch) except actions.BadAction, e: log.error("error preparing action for %s: %s" %\ (conninfo(hi, rmnames), str(e))) if not action: log.debug(2, "No actions for %s" % (conninfo(hi, rmnames),)) proc.closesock(newsock) return # Actions have two components: messages to log, and something to # do. Either can be blank (hopefully both are not blank, but). for le in action.logmsgs: log.report(le) # Activate the action's work function (if any) in a separate # process and track it if necessary. We do not track the message # processes, since they are expected to die fast and we can do # without the churning of data structures in the parent. if action.what: func = whatToFunc[action.what] try: pid = proc.forkaction(newsock, func, action)