mirror of
				https://github.com/Mikaela/Limnoria.git
				synced 2025-10-31 15:47:25 +01:00 
			
		
		
		
	Create a commands.process function which runs a function inside a separate process.
This is the only way to limit the execution time of a possibly long-running python statement. Use this on String.re, due to the possibility of pathologically long re matching in python. This allows us to remove the 'trusted-only' restriction on string.re. In the future, this should probably be used in other places that take user-supplied regexps, such as 'misc last --regexp', for example, as well as other potentially long-running tasks that can block the bot. Signed-off-by: James McCoy <jamessan@users.sourceforge.net>
This commit is contained in:
		
							parent
							
								
									d691a91636
								
							
						
					
					
						commit
						a2985c37d6
					
				| @ -34,10 +34,10 @@ import binascii | ||||
| import supybot.utils as utils | ||||
| from supybot.commands import * | ||||
| import supybot.plugins as plugins | ||||
| import supybot.commands as commands | ||||
| import supybot.ircutils as ircutils | ||||
| import supybot.callbacks as callbacks | ||||
| 
 | ||||
| 
 | ||||
| class String(callbacks.Plugin): | ||||
|     def ord(self, irc, msg, args, letter): | ||||
|         """<letter> | ||||
| @ -141,10 +141,10 @@ class String(callbacks.Plugin): | ||||
|             s = 'You probably don\'t want to match the empty string.' | ||||
|             irc.error(s) | ||||
|         else: | ||||
|             irc.reply(f(text)) | ||||
|     re = wrap(re, [('checkCapability', 'trusted'), | ||||
|                    first('regexpMatcher', 'regexpReplacer'), | ||||
|                    'text']) | ||||
|             v = commands.process(f, text, timeout=10) | ||||
|             irc.reply(v) | ||||
|     re = thread(wrap(re, [first('regexpMatcher', 'regexpReplacer'), | ||||
|                    'text'])) | ||||
| 
 | ||||
|     def xor(self, irc, msg, args, password, text): | ||||
|         """<password> <text> | ||||
|  | ||||
| @ -970,6 +970,23 @@ class CommandThread(world.SupyThread): | ||||
|         finally: | ||||
|             self.cb.threaded = self.originalThreaded | ||||
| 
 | ||||
| class CommandProcess(world.SupyProcess): | ||||
|     """Just does some extra logging and error-recovery for commands that need | ||||
|     to run in processes. | ||||
|     """ | ||||
|     def __init__(self, target=None, args=(), kwargs={}): | ||||
|         self.command = args[0] | ||||
|         self.cb = target.im_self | ||||
|         procName = 'Process #%s (for %s.%s)' % (world.processesSpawned, | ||||
|                                                  self.cb.name(), | ||||
|                                                  self.command) | ||||
|         log.debug('Spawning process %s (args: %r)', procName, args) | ||||
|         self.__parent = super(CommandProcess, self) | ||||
|         self.__parent.__init__(target=target, name=procName, | ||||
|                                args=args, kwargs=kwargs) | ||||
| 
 | ||||
|     def run(self): | ||||
|         self.__parent.run() | ||||
| 
 | ||||
| class CanonicalString(registry.NormalizedString): | ||||
|     def normalize(self, s): | ||||
|  | ||||
| @ -33,10 +33,12 @@ Includes wrappers for commands. | ||||
| """ | ||||
| 
 | ||||
| import time | ||||
| import Queue | ||||
| import types | ||||
| import getopt | ||||
| import inspect | ||||
| import threading | ||||
| import multiprocessing | ||||
| 
 | ||||
| from . import callbacks, conf, ircdb, ircmsgs, ircutils, log, utils, world | ||||
| 
 | ||||
| @ -59,6 +61,29 @@ def thread(f): | ||||
|             f(self, irc, msg, args, *L, **kwargs) | ||||
|     return utils.python.changeFunctionName(newf, f.func_name, f.__doc__) | ||||
| 
 | ||||
| def process(f, *args, **kwargs): | ||||
|     """Runs a function in a subprocess. | ||||
|     Takes an extra timeout argument, which, if supplied, limits the length | ||||
|     of execution of target function to <timeout> seconds.""" | ||||
|     timeout = kwargs.pop('timeout') | ||||
|     q = multiprocessing.Queue() | ||||
|     def newf(f, q, *args, **kwargs): | ||||
|         r = f(*args, **kwargs) | ||||
|         q.put(r) | ||||
|     targetArgs = (f, q,) + args | ||||
|     p = world.SupyProcess(target=newf, | ||||
|                                 args=targetArgs, kwargs=kwargs) | ||||
|     p.start() | ||||
|     p.join(timeout) | ||||
|     if p.is_alive(): | ||||
|         p.terminate() | ||||
|         q.put("Function call aborted due to timeout.") | ||||
|     try: | ||||
|         v = q.get(block=False) | ||||
|     except Queue.Empty: | ||||
|         v = "Nothing returned." | ||||
|     return v | ||||
| 
 | ||||
| class UrlSnarfThread(world.SupyThread): | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         assert 'url' in kwargs | ||||
|  | ||||
							
								
								
									
										10
									
								
								src/world.py
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/world.py
									
									
									
									
									
								
							| @ -37,6 +37,7 @@ import sys | ||||
| import time | ||||
| import atexit | ||||
| import threading | ||||
| import multiprocessing | ||||
| 
 | ||||
| if sys.version_info >= (2, 5, 0): | ||||
|     import re as sre | ||||
| @ -63,6 +64,15 @@ class SupyThread(threading.Thread): | ||||
|         super(SupyThread, self).__init__(*args, **kwargs) | ||||
|         log.debug('Spawning thread %q.', self.getName()) | ||||
| 
 | ||||
| processesSpawned = 1 # Starts at one for the initial process. | ||||
| class SupyProcess(multiprocessing.Process): | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         global processesSpawned | ||||
|         processesSpawned += 1 | ||||
|         super(SupyProcess, self).__init__(*args, **kwargs) | ||||
|         log.debug('Spawning process %q.', self.name) | ||||
| 
 | ||||
| 
 | ||||
| commandsProcessed = 0 | ||||
| 
 | ||||
| ircs = [] # A list of all the IRCs. | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Daniel Folkinshteyn
						Daniel Folkinshteyn