[or-cvs] r22375: {arm} Moved reset functionality (issuing and listening) into tor u (in arm/trunk: interface util)
Damian Johnson
atagar1 at gmail.com
Thu May 20 16:26:47 UTC 2010
Author: atagar
Date: 2010-05-20 16:26:46 +0000 (Thu, 20 May 2010)
New Revision: 22375
Modified:
arm/trunk/interface/controller.py
arm/trunk/util/torTools.py
Log:
Moved reset functionality (issuing and listening) into tor util and fixed minor bug with issuing sighups (it uses regex matches rather than absolute).
Modified: arm/trunk/interface/controller.py
===================================================================
--- arm/trunk/interface/controller.py 2010-05-19 16:04:14 UTC (rev 22374)
+++ arm/trunk/interface/controller.py 2010-05-20 16:26:46 UTC (rev 22375)
@@ -310,10 +310,7 @@
except curses.error: pass
# attempts to determine tor's current pid (left as None if unresolveable, logging an error later)
- controlPort = None
- try: controlPort = int(conn.get_option("ControlPort")[0][1])
- except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): pass
- torPid = torTools.getPid(controlPort)
+ torPid = torTools.getConn().getPid()
try:
confLocation = conn.get_info("config-file")["config-file"]
@@ -1192,57 +1189,14 @@
confirmationKey = stdscr.getch()
if confirmationKey in (ord('x'), ord('X')):
try:
- conn.send_signal("RELOAD")
- except Exception, err:
- # new torrc parameters caused an error (tor's likely shut down)
- # BUG: this doesn't work - torrc errors still cause TorCtl to crash... :(
- # http://bugs.noreply.org/flyspray/index.php?do=details&id=1329
- log.log(log.ERR, "Error detected when reloading tor: %s" % str(err))
- pass
-
- # The following issues a sighup via a system command (Sebastian
- # mentioned later that sending a RELOAD signal is equivilant, which
- # is of course far preferable).
-
- """
- try:
- # Redirects stderr to stdout so we can check error status (output
- # should be empty if successful). Example error:
- # pkill: 5592 - Operation not permitted
- #
- # note that this may provide multiple errors, even if successful,
- # hence this:
- # - only provide an error if Tor fails to log a sighup
- # - provide the error message associated with the tor pid (others
- # would be a red herring)
- pkillCall = os.popen("pkill -sighup tor 2> /dev/stdout")
- pkillOutput = pkillCall.readlines()
- pkillCall.close()
+ torTools.getConn().reload()
+ except IOError, exc:
+ log.log(log.ERR, "Error detected when reloading tor: %s" % str(exc))
- # Give the sighupTracker a moment to detect the sighup signal. This
- # is, of course, a possible concurrency bug. However I'm not sure
- # of a better method for blocking on this...
- waitStart = time.time()
- while time.time() - waitStart < 1:
- time.sleep(0.1)
- if sighupTracker.isReset: break
-
- if not sighupTracker.isReset:
- errorLine = ""
- if torPid:
- for line in pkillOutput:
- if line.startswith("pkill: %s - " % torPid):
- errorLine = line
- break
-
- if errorLine: raise IOError(" ".join(errorLine.split()[3:]))
- else: raise IOError()
- except IOError, err:
- errorMsg = " (%s)" % str(err) if str(err) else ""
- panels["control"].setMsg("Sighup failed%s" % errorMsg, curses.A_STANDOUT)
- panels["control"].redraw()
- time.sleep(2)
- """
+ #errorMsg = " (%s)" % str(err) if str(err) else ""
+ #panels["control"].setMsg("Sighup failed%s" % errorMsg, curses.A_STANDOUT)
+ #panels["control"].redraw()
+ #time.sleep(2)
# reverts display settings
curses.halfdelay(REFRESH_RATE * 10)
Modified: arm/trunk/util/torTools.py
===================================================================
--- arm/trunk/util/torTools.py 2010-05-19 16:04:14 UTC (rev 22374)
+++ arm/trunk/util/torTools.py 2010-05-20 16:26:46 UTC (rev 22375)
@@ -9,6 +9,8 @@
'0.2.1.24'
"""
+import os
+import time
import socket
import getpass
import thread
@@ -21,14 +23,15 @@
# enums for tor's controller state:
# TOR_INIT - attached to a new controller
-# TOR_RESTART - restart or sighup signal received by tor (resetting internal state)
+# TOR_RESET - restart or sighup signal received by tor (resetting internal state)
# TOR_CLOSED - control port closed
-TOR_INIT, TOR_RESTART, TOR_CLOSED = range(1, 4)
+TOR_INIT, TOR_RESET, TOR_CLOSED = range(1, 4)
# Message logged by default when a controller event type can't be set (message
# has the event type inserted into it). This skips logging entirely if None.
DEFAULT_FAILED_EVENT_ENTRY = (log.WARN, "Unsupported event type: %s")
+# TODO: check version when reattaching to controller and if version changes, flush?
# Skips attempting to set events we've failed to set before. This avoids
# logging duplicate warnings but can be problematic if controllers belonging
# to multiple versions of tor are attached, making this unreflective of the
@@ -239,7 +242,7 @@
return CONTROLLER
# TODO: sighup notification (and replacement in controller!)
-class Controller:
+class Controller (TorCtl.PostEventListener):
"""
TorCtl wrapper providing convenience functions, listener functionality for
tor's state, and the capability for controller connections to be restarted
@@ -247,10 +250,16 @@
"""
def __init__(self):
+ TorCtl.PostEventListener.__init__(self)
self.conn = None # None if uninitialized or controller's been closed
self.connLock = threading.RLock()
self.listeners = [] # callback functions for tor's state changes
self.controllerEvents = {} # mapping of successfully set controller events to their failure level/msg
+ self._isReset = False # internal flag for tracking resets
+
+ # cached information static for a connection (None if unset, "UNKNOWN" if
+ # unable to be determined)
+ self.pid = None
def init(self, conn):
"""
@@ -261,9 +270,17 @@
conn - TorCtl instance to be used
"""
- if conn.is_live():
+ if conn.is_live() and conn != self.conn:
+ if self.conn: self.close() # shut down current connection
+
self.connLock.acquire()
self.conn = conn
+ self.conn.add_event_listener(self)
+
+ # sets the events listened for by the new controller (incompatable events
+ # are dropped with a logged warning)
+ self.setControllerEvents(self.controllerEvents)
+
self.connLock.release()
# notifies listeners that a new controller is available
@@ -278,6 +295,7 @@
if self.conn:
self.conn.close()
self.conn = None
+ self.pid = None
self.connLock.release()
# notifies listeners that the controller's been shut down
@@ -372,6 +390,25 @@
if not suppressExc and raisedExc: raise raisedExc
else: return result
+ def getPid(self):
+ """
+ Provides the pid of the attached tor process (None if no controller exists
+ or this can't be determined).
+ """
+
+ self.connLock.acquire()
+
+ if self.pid == "UNKNOWN" or not self.isAlive(): result = None
+ elif self.pid: result = self.pid
+ else:
+ result = getPid(int(self.getOption("ControlPort", 9051)))
+ if result: self.pid = result
+ else: self.pid = "UNKNOWN"
+
+ self.connLock.release()
+
+ return result
+
def addStatusListener(self, callback):
"""
Directs further events related to tor's controller status to the callback
@@ -418,10 +455,10 @@
returnVal = []
if self.isAlive():
+ events = set(eventsToMsg.keys())
unavailableEvents = set()
# removes anything we've already failed to set
- events = set(eventsToMsg.keys())
if DROP_FAILED_EVENTS:
unavailableEvents.update(events.intersection(FAILED_EVENTS))
events.difference_update(FAILED_EVENTS)
@@ -478,6 +515,89 @@
self.connLock.release()
return returnVal
+ def reload(self, issueSighup = False):
+ """
+ This resets tor (sending a RELOAD signal to the control port) causing tor's
+ internal state to be reset and the torrc reloaded. This can either be done
+ by...
+ - the controller via a RELOAD signal (default and suggested)
+ conn.send_signal("RELOAD")
+ - system reload signal (hup)
+ pkill -sighup tor
+
+ The later isn't really useful unless there's some reason the RELOAD signal
+ won't do the trick. Both methods raise an IOError in case of failure.
+
+ Arguments:
+ issueSighup - issues a sighup rather than a controller RELOAD signal
+ """
+
+ self.connLock.acquire()
+
+ raisedException = None
+ if self.isAlive():
+ if not issueSighup:
+ try:
+ self.conn.send_signal("RELOAD")
+ except Exception, exc:
+ # new torrc parameters caused an error (tor's likely shut down)
+ # BUG: this doesn't work - torrc errors still cause TorCtl to crash... :(
+ # http://bugs.noreply.org/flyspray/index.php?do=details&id=1329
+ raisedException = IOError(str(exc))
+ else:
+ try:
+ # Redirects stderr to stdout so we can check error status (output
+ # should be empty if successful). Example error:
+ # pkill: 5592 - Operation not permitted
+ #
+ # note that this may provide multiple errors, even if successful,
+ # hence this:
+ # - only provide an error if Tor fails to log a sighup
+ # - provide the error message associated with the tor pid (others
+ # would be a red herring)
+ if not sysTools.isAvailable("pkill"):
+ raise IOError("pkill command is unavailable")
+
+ self._isReset = False
+ pkillCall = os.popen("pkill -sighup ^tor$ 2> /dev/stdout")
+ pkillOutput = pkillCall.readlines()
+ pkillCall.close()
+
+ # Give the sighupTracker a moment to detect the sighup signal. This
+ # is, of course, a possible concurrency bug. However I'm not sure
+ # of a better method for blocking on this...
+ waitStart = time.time()
+ while time.time() - waitStart < 1:
+ time.sleep(0.1)
+ if self._isReset: break
+
+ if not self._isReset:
+ errorLine, torPid = "", self.getPid()
+ if torPid:
+ for line in pkillOutput:
+ if line.startswith("pkill: %s - " % torPid):
+ errorLine = line
+ break
+
+ if errorLine: raise IOError(" ".join(errorLine.split()[3:]))
+ else: raise IOError("failed silently")
+ except IOError, exc:
+ raisedException = exc
+
+ self.connLock.release()
+
+ if raisedException: raise raisedException
+
+ def msg_event(self, event):
+ """
+ Listens for reload signal (hup), which is either produced by:
+ causing the torrc and internal state to be reset.
+ """
+
+ if event.level == "NOTICE" and event.msg.startswith("Received reload signal (hup)"):
+ self._isReset = True
+ thread.start_new_thread(self._notifyStatusListeners, (TOR_RESET,))
+
def _notifyStatusListeners(self, eventType):
"""
Sends a notice to all current listeners that a given change in tor's
More information about the tor-commits
mailing list