示例#1
0
    def load(self, logFailure=False):
        """
    Loads or reloads the torrc contents, raising an IOError if there's a
    problem.
    
    Arguments:
      logFailure - if the torrc fails to load and we've never provided a
                   warning for this before then logs a warning
    """

        self.valsLock.acquire()

        # clears contents and caches
        self.contents, self.configLocation = None, None
        self.displayableContents = None
        self.strippedContents = None
        self.corrections = None

        try:
            self.configLocation = getConfigLocation()
            configFile = open(self.configLocation, "r")
            self.contents = configFile.readlines()
            configFile.close()
        except IOError, exc:
            if logFailure and not self.isLoadFailWarned:
                log.warn("Unable to load torrc (%s)" %
                         sysTools.getFileErrorMsg(exc))
                self.isLoadFailWarned = True

            self.valsLock.release()
            raise exc
示例#2
0
文件: torConfig.py 项目: twilde/arm
 def load(self, logFailure = False):
   """
   Loads or reloads the torrc contents, raising an IOError if there's a
   problem.
   
   Arguments:
     logFailure - if the torrc fails to load and we've never provided a
                  warning for this before then logs a warning
   """
   
   self.valsLock.acquire()
   
   # clears contents and caches
   self.contents, self.configLocation = None, None
   self.displayableContents = None
   self.strippedContents = None
   self.corrections = None
   
   try:
     self.configLocation = getConfigLocation()
     configFile = open(self.configLocation, "r")
     self.contents = configFile.readlines()
     configFile.close()
   except IOError, exc:
     if logFailure and not self.isLoadFailWarned:
       msg = "Unable to load torrc (%s)" % sysTools.getFileErrorMsg(exc)
       log.log(CONFIG["log.torrc.readFailed"], msg)
       self.isLoadFailWarned = True
     
     self.valsLock.release()
     raise exc
示例#3
0
文件: logPanel.py 项目: refnode/arm
 def showSnapshotPrompt(self):
   """
   Lets user enter a path to take a snapshot, canceling if left blank.
   """
   
   pathInput = popups.inputPrompt("Path to save log snapshot: ")
   
   if pathInput:
     try:
       self.saveSnapshot(pathInput)
       popups.showMsg("Saved: %s" % pathInput, 2)
     except IOError, exc:
       popups.showMsg("Unable to save snapshot: %s" % sysTools.getFileErrorMsg(exc), 2)
示例#4
0
文件: logPanel.py 项目: zhou-kz/arm
    def registerEvent(self, event):
        """
    Notes event and redraws log. If paused it's held in a temporary buffer.
    
    Arguments:
      event - LogEntry for the event that occurred
    """

        if not event.type in self.loggedEvents: return

        # strips control characters to avoid screwing up the terminal
        event.msg = uiTools.getPrintable(event.msg)

        # note event in the log file if we're saving them
        if self.logFile:
            try:
                self.logFile.write(event.getDisplayMessage(True) + "\n")
                self.logFile.flush()
            except IOError, exc:
                log.log(
                    self._config["log.logPanel.logFileWriteFailed"],
                    "Unable to write to log file: %s" %
                    sysTools.getFileErrorMsg(exc))
                self.logFile = None
示例#5
0
 def showWriteDialog(self):
   """
   Provies an interface to confirm if the configuration is saved and, if so,
   where.
   """
   
   # display a popup for saving the current configuration
   configLines = torConfig.getCustomOptions(True)
   popup, width, height = popups.init(len(configLines) + 2)
   if not popup: return
   
   try:
     # displayed options (truncating the labels if there's limited room)
     if width >= 30: selectionOptions = ("Save", "Save As...", "Cancel")
     else: selectionOptions = ("Save", "Save As", "X")
     
     # checks if we can show options beside the last line of visible content
     isOptionLineSeparate = False
     lastIndex = min(height - 2, len(configLines) - 1)
     
     # if we don't have room to display the selection options and room to
     # grow then display the selection options on its own line
     if width < (30 + len(configLines[lastIndex])):
       popup.setHeight(height + 1)
       popup.redraw(True) # recreates the window instance
       newHeight, _ = popup.getPreferredSize()
       
       if newHeight > height:
         height = newHeight
         isOptionLineSeparate = True
     
     key, selection = 0, 2
     while not uiTools.isSelectionKey(key):
       # if the popup has been resized then recreate it (needed for the
       # proper border height)
       newHeight, newWidth = popup.getPreferredSize()
       if (height, width) != (newHeight, newWidth):
         height, width = newHeight, newWidth
         popup.redraw(True)
       
       # if there isn't room to display the popup then cancel it
       if height <= 2:
         selection = 2
         break
       
       popup.win.erase()
       popup.win.box()
       popup.addstr(0, 0, "Configuration being saved:", curses.A_STANDOUT)
       
       visibleConfigLines = height - 3 if isOptionLineSeparate else height - 2
       for i in range(visibleConfigLines):
         line = uiTools.cropStr(configLines[i], width - 2)
         
         if " " in line:
           option, arg = line.split(" ", 1)
           popup.addstr(i + 1, 1, option, curses.A_BOLD | uiTools.getColor("green"))
           popup.addstr(i + 1, len(option) + 2, arg, curses.A_BOLD | uiTools.getColor("cyan"))
         else:
           popup.addstr(i + 1, 1, line, curses.A_BOLD | uiTools.getColor("green"))
       
       # draws selection options (drawn right to left)
       drawX = width - 1
       for i in range(len(selectionOptions) - 1, -1, -1):
         optionLabel = selectionOptions[i]
         drawX -= (len(optionLabel) + 2)
         
         # if we've run out of room then drop the option (this will only
         # occure on tiny displays)
         if drawX < 1: break
         
         selectionFormat = curses.A_STANDOUT if i == selection else curses.A_NORMAL
         popup.addstr(height - 2, drawX, "[")
         popup.addstr(height - 2, drawX + 1, optionLabel, selectionFormat | curses.A_BOLD)
         popup.addstr(height - 2, drawX + len(optionLabel) + 1, "]")
         
         drawX -= 1 # space gap between the options
       
       popup.win.refresh()
       
       key = cli.controller.getController().getScreen().getch()
       if key == curses.KEY_LEFT: selection = max(0, selection - 1)
       elif key == curses.KEY_RIGHT: selection = min(len(selectionOptions) - 1, selection + 1)
     
     if selection in (0, 1):
       loadedTorrc, promptCanceled = torConfig.getTorrc(), False
       try: configLocation = loadedTorrc.getConfigLocation()
       except IOError: configLocation = ""
       
       if selection == 1:
         # prompts user for a configuration location
         configLocation = popups.inputPrompt("Save to (esc to cancel): ", configLocation)
         if not configLocation: promptCanceled = True
       
       if not promptCanceled:
         try:
           torConfig.saveConf(configLocation, configLines)
           msg = "Saved configuration to %s" % configLocation
         except IOError, exc:
           msg = "Unable to save configuration (%s)" % sysTools.getFileErrorMsg(exc)
         
         popups.showMsg(msg, 2)
   finally: popups.finalize()
示例#6
0
def drawTorMonitor(stdscr, startTime):
    """
  Main draw loop context.
  
  Arguments:
    stdscr    - curses window
    startTime - unix time for when arm was started
  """

    initController(stdscr, startTime)
    control = getController()

    # provides notice about any unused config keys
    for key in conf.getConfig("arm").getUnusedKeys():
        log.log(CONFIG["log.configEntryUndefined"],
                "Unused configuration entry: %s" % key)

    # tells daemon panels to start
    for panelImpl in control.getDaemonPanels():
        panelImpl.start()

    # allows for background transparency
    try:
        curses.use_default_colors()
    except curses.error:
        pass

    # makes the cursor invisible
    try:
        curses.curs_set(0)
    except curses.error:
        pass

    # logs the initialization time
    msg = "arm started (initialization took %0.3f seconds)" % (time.time() -
                                                               startTime)
    log.log(CONFIG["log.startTime"], msg)

    # main draw loop
    overrideKey = None  # uses this rather than waiting on user input
    isUnresponsive = False  # flag for heartbeat responsiveness check
    if not torTools.getConn().isAlive(): overrideKey = ord('w')  # shows wizard

    while not control.isDone():
        displayPanels = control.getDisplayPanels()
        isUnresponsive = heartbeatCheck(isUnresponsive)

        # sets panel visability
        for panelImpl in control.getAllPanels():
            panelImpl.setVisible(panelImpl in displayPanels)

        # redraws the interface if it's needed
        control.redraw(False)
        stdscr.refresh()

        # wait for user keyboard input until timeout, unless an override was set
        if overrideKey:
            key, overrideKey = overrideKey, None
        else:
            curses.halfdelay(CONFIG["features.redrawRate"] * 10)
            key = stdscr.getch()

        if key == curses.KEY_RIGHT:
            control.nextPage()
        elif key == curses.KEY_LEFT:
            control.prevPage()
        elif key == ord('p') or key == ord('P'):
            control.setPaused(not control.isPaused())
        elif key == ord('m') or key == ord('M'):
            cli.menu.menu.showMenu()
        elif key == ord('q') or key == ord('Q'):
            # provides prompt to confirm that arm should exit
            if CONFIG["features.confirmQuit"]:
                msg = "Are you sure (q again to confirm)?"
                confirmationKey = cli.popups.showMsg(msg, attr=curses.A_BOLD)
                quitConfirmed = confirmationKey in (ord('q'), ord('Q'))
            else:
                quitConfirmed = True

            if quitConfirmed: control.quit()
        elif key == ord('x') or key == ord('X'):
            # provides prompt to confirm that arm should issue a sighup
            msg = "This will reset Tor's internal state. Are you sure (x again to confirm)?"
            confirmationKey = cli.popups.showMsg(msg, attr=curses.A_BOLD)

            if confirmationKey in (ord('x'), ord('X')):
                try:
                    torTools.getConn().reload()
                except IOError, exc:
                    log.log(
                        log.ERR, "Error detected when reloading tor: %s" %
                        sysTools.getFileErrorMsg(exc))
        elif key == ord('h') or key == ord('H'):
            overrideKey = cli.popups.showHelpPopup()
示例#7
0
文件: controller.py 项目: gsathya/arm
def drawTorMonitor(stdscr, startTime):
    """
  Main draw loop context.
  
  Arguments:
    stdscr    - curses window
    startTime - unix time for when arm was started
  """

    initController(stdscr, startTime)
    control = getController()

    # provides notice about any unused config keys
    for key in conf.get_config("arm").unused_keys():
        log.notice("Unused configuration entry: %s" % key)

    # tells daemon panels to start
    for panelImpl in control.getDaemonPanels():
        panelImpl.start()

    # allows for background transparency
    try:
        curses.use_default_colors()
    except curses.error:
        pass

    # makes the cursor invisible
    try:
        curses.curs_set(0)
    except curses.error:
        pass

    # logs the initialization time
    log.info("arm started (initialization took %0.3f seconds)" % (time.time() - startTime))

    # main draw loop
    overrideKey = None  # uses this rather than waiting on user input
    isUnresponsive = False  # flag for heartbeat responsiveness check

    while not control.isDone():
        displayPanels = control.getDisplayPanels()
        isUnresponsive = heartbeatCheck(isUnresponsive)

        # sets panel visability
        for panelImpl in control.getAllPanels():
            panelImpl.setVisible(panelImpl in displayPanels)

        # redraws the interface if it's needed
        control.redraw(False)
        stdscr.refresh()

        # wait for user keyboard input until timeout, unless an override was set
        if overrideKey:
            key, overrideKey = overrideKey, None
        else:
            curses.halfdelay(CONFIG["features.redrawRate"] * 10)
            key = stdscr.getch()

        if key == curses.KEY_RIGHT:
            control.nextPage()
        elif key == curses.KEY_LEFT:
            control.prevPage()
        elif key == ord("p") or key == ord("P"):
            control.setPaused(not control.isPaused())
        elif key == ord("m") or key == ord("M"):
            cli.menu.menu.showMenu()
        elif key == ord("q") or key == ord("Q"):
            # provides prompt to confirm that arm should exit
            if CONFIG["features.confirmQuit"]:
                msg = "Are you sure (q again to confirm)?"
                confirmationKey = cli.popups.showMsg(msg, attr=curses.A_BOLD)
                quitConfirmed = confirmationKey in (ord("q"), ord("Q"))
            else:
                quitConfirmed = True

            if quitConfirmed:
                control.quit()
        elif key == ord("x") or key == ord("X"):
            # provides prompt to confirm that arm should issue a sighup
            msg = "This will reset Tor's internal state. Are you sure (x again to confirm)?"
            confirmationKey = cli.popups.showMsg(msg, attr=curses.A_BOLD)

            if confirmationKey in (ord("x"), ord("X")):
                try:
                    torTools.getConn().reload()
                except IOError, exc:
                    log.error("Error detected when reloading tor: %s" % sysTools.getFileErrorMsg(exc))
        elif key == ord("h") or key == ord("H"):
            overrideKey = cli.popups.showHelpPopup()
示例#8
0
文件: logPanel.py 项目: refnode/arm
 def registerEvent(self, event):
   """
   Notes event and redraws log. If paused it's held in a temporary buffer.
   
   Arguments:
     event - LogEntry for the event that occurred
   """
   
   if not event.type in self.loggedEvents: return
   
   # strips control characters to avoid screwing up the terminal
   event.msg = uiTools.getPrintable(event.msg)
   
   # note event in the log file if we're saving them
   if self.logFile:
     try:
       self.logFile.write(event.getDisplayMessage(True) + "\n")
       self.logFile.flush()
     except IOError, exc:
       log.log(self._config["log.logPanel.logFileWriteFailed"], "Unable to write to log file: %s" % sysTools.getFileErrorMsg(exc))
       self.logFile = None
示例#9
0
文件: logPanel.py 项目: katmagic/arm
 def __init__(self, stdscr, loggedEvents, config=None):
   panel.Panel.__init__(self, stdscr, "log", 0)
   threading.Thread.__init__(self)
   self.setDaemon(True)
   
   self._config = dict(DEFAULT_CONFIG)
   
   if config:
     config.update(self._config, {
       "features.log.maxLinesPerEntry": 1,
       "features.log.prepopulateReadLimit": 0,
       "features.log.maxRefreshRate": 10,
       "cache.logPanel.size": 1000})
   
   # collapses duplicate log entries if false, showing only the most recent
   self.showDuplicates = self._config["features.log.showDuplicateEntries"]
   
   self.msgLog = []                    # log entries, sorted by the timestamp
   self.loggedEvents = loggedEvents    # events we're listening to
   self.regexFilter = None             # filter for presented log events (no filtering if None)
   self.lastContentHeight = 0          # height of the rendered content when last drawn
   self.logFile = None                 # file log messages are saved to (skipped if None)
   self.scroll = 0
   self._isPaused = False
   self._pauseBuffer = []              # location where messages are buffered if paused
   
   self._lastUpdate = -1               # time the content was last revised
   self._halt = False                  # terminates thread if true
   self._cond = threading.Condition()  # used for pausing/resuming the thread
   
   # restricts concurrent write access to attributes used to draw the display
   # and pausing:
   # msgLog, loggedEvents, regexFilter, scroll, _pauseBuffer
   self.valsLock = threading.RLock()
   
   # cached parameters (invalidated if arguments for them change)
   # last set of events we've drawn with
   self._lastLoggedEvents = []
   
   # _getTitle (args: loggedEvents, regexFilter pattern, width)
   self._titleCache = None
   self._titleArgs = (None, None, None)
   
   # fetches past tor events from log file, if available
   torEventBacklog = []
   if self._config["features.log.prepopulate"]:
     setRunlevels = list(set.intersection(set(self.loggedEvents), set(RUNLEVELS)))
     readLimit = self._config["features.log.prepopulateReadLimit"]
     addLimit = self._config["cache.logPanel.size"]
     torEventBacklog = getLogFileEntries(setRunlevels, readLimit, addLimit, self._config)
   
   # adds arm listener and fetches past events
   log.LOG_LOCK.acquire()
   try:
     armRunlevels = [log.DEBUG, log.INFO, log.NOTICE, log.WARN, log.ERR]
     log.addListeners(armRunlevels, self._registerArmEvent)
     
     # gets the set of arm events we're logging
     setRunlevels = []
     for i in range(len(armRunlevels)):
       if "ARM_" + RUNLEVELS[i] in self.loggedEvents:
         setRunlevels.append(armRunlevels[i])
     
     armEventBacklog = []
     for level, msg, eventTime in log._getEntries(setRunlevels):
       runlevelStr = log.RUNLEVEL_STR[level]
       armEventEntry = LogEntry(eventTime, "ARM_" + runlevelStr, msg, RUNLEVEL_EVENT_COLOR[runlevelStr])
       armEventBacklog.insert(0, armEventEntry)
     
     # joins armEventBacklog and torEventBacklog chronologically into msgLog
     while armEventBacklog or torEventBacklog:
       if not armEventBacklog:
         self.msgLog.append(torEventBacklog.pop(0))
       elif not torEventBacklog:
         self.msgLog.append(armEventBacklog.pop(0))
       elif armEventBacklog[0].timestamp < torEventBacklog[0].timestamp:
         self.msgLog.append(torEventBacklog.pop(0))
       else:
         self.msgLog.append(armEventBacklog.pop(0))
   finally:
     log.LOG_LOCK.release()
   
   # crops events that are either too old, or more numerous than the caching size
   self._trimEvents(self.msgLog)
   
   # leaving lastContentHeight as being too low causes initialization problems
   self.lastContentHeight = len(self.msgLog)
   
   # adds listeners for tor and torctl events
   conn = torTools.getConn()
   conn.addEventListener(TorEventObserver(self.registerEvent))
   conn.addTorCtlListener(self._registerTorCtlEvent)
   
   # opens log file if we'll be saving entries
   if self._config["features.logFile"]:
     logPath = self._config["features.logFile"]
     
     # make dir if the path doesn't already exist
     baseDir = os.path.dirname(logPath)
     if not os.path.exists(baseDir): os.makedirs(baseDir)
     
     try:
       self.logFile = open(logPath, "a")
       log.log(self._config["log.logPanel.logFileOpened"], "arm %s opening log file (%s)" % (VERSION, logPath))
     except IOError, exc:
       log.log(self._config["log.logPanel.logFileWriteFailed"], "Unable to write to log file: %s" % sysTools.getFileErrorMsg(exc))
       self.logFile = None
示例#10
0
 def showWriteDialog(self):
   """
   Provies an interface to confirm if the configuration is saved and, if so,
   where.
   """
   
   # display a popup for saving the current configuration
   configLines = torConfig.getCustomOptions(True)
   popup, width, height = popups.init(len(configLines) + 2)
   if not popup: return
   
   try:
     # displayed options (truncating the labels if there's limited room)
     if width >= 30: selectionOptions = ("Save", "Save As...", "Cancel")
     else: selectionOptions = ("Save", "Save As", "X")
     
     # checks if we can show options beside the last line of visible content
     isOptionLineSeparate = False
     lastIndex = min(height - 2, len(configLines) - 1)
     
     # if we don't have room to display the selection options and room to
     # grow then display the selection options on its own line
     if width < (30 + len(configLines[lastIndex])):
       popup.setHeight(height + 1)
       popup.redraw(True) # recreates the window instance
       newHeight, _ = popup.getPreferredSize()
       
       if newHeight > height:
         height = newHeight
         isOptionLineSeparate = True
     
     key, selection = 0, 2
     while not uiTools.isSelectionKey(key):
       # if the popup has been resized then recreate it (needed for the
       # proper border height)
       newHeight, newWidth = popup.getPreferredSize()
       if (height, width) != (newHeight, newWidth):
         height, width = newHeight, newWidth
         popup.redraw(True)
       
       # if there isn't room to display the popup then cancel it
       if height <= 2:
         selection = 2
         break
       
       popup.win.erase()
       popup.win.box()
       popup.addstr(0, 0, "Configuration being saved:", curses.A_STANDOUT)
       
       visibleConfigLines = height - 3 if isOptionLineSeparate else height - 2
       for i in range(visibleConfigLines):
         line = uiTools.cropStr(configLines[i], width - 2)
         
         if " " in line:
           option, arg = line.split(" ", 1)
           popup.addstr(i + 1, 1, option, curses.A_BOLD | uiTools.getColor("green"))
           popup.addstr(i + 1, len(option) + 2, arg, curses.A_BOLD | uiTools.getColor("cyan"))
         else:
           popup.addstr(i + 1, 1, line, curses.A_BOLD | uiTools.getColor("green"))
       
       # draws selection options (drawn right to left)
       drawX = width - 1
       for i in range(len(selectionOptions) - 1, -1, -1):
         optionLabel = selectionOptions[i]
         drawX -= (len(optionLabel) + 2)
         
         # if we've run out of room then drop the option (this will only
         # occure on tiny displays)
         if drawX < 1: break
         
         selectionFormat = curses.A_STANDOUT if i == selection else curses.A_NORMAL
         popup.addstr(height - 2, drawX, "[")
         popup.addstr(height - 2, drawX + 1, optionLabel, selectionFormat | curses.A_BOLD)
         popup.addstr(height - 2, drawX + len(optionLabel) + 1, "]")
         
         drawX -= 1 # space gap between the options
       
       popup.win.refresh()
       
       key = cli.controller.getController().getScreen().getch()
       if key == curses.KEY_LEFT: selection = max(0, selection - 1)
       elif key == curses.KEY_RIGHT: selection = min(len(selectionOptions) - 1, selection + 1)
     
     if selection in (0, 1):
       loadedTorrc, promptCanceled = torConfig.getTorrc(), False
       try: configLocation = loadedTorrc.getConfigLocation()
       except IOError: configLocation = ""
       
       if selection == 1:
         # prompts user for a configuration location
         configLocation = popups.inputPrompt("Save to (esc to cancel): ", configLocation)
         if not configLocation: promptCanceled = True
       
       if not promptCanceled:
         try:
           torConfig.saveConf(configLocation, configLines)
           msg = "Saved configuration to %s" % configLocation
         except IOError, exc:
           msg = "Unable to save configuration (%s)" % sysTools.getFileErrorMsg(exc)
         
         popups.showMsg(msg, 2)
   finally: popups.finalize()
示例#11
0
文件: logPanel.py 项目: zhou-kz/arm
    def __init__(self, stdscr, loggedEvents, config=None):
        panel.Panel.__init__(self, stdscr, "log", 0)
        threading.Thread.__init__(self)
        self.setDaemon(True)

        self._config = dict(DEFAULT_CONFIG)

        if config:
            config.update(
                self._config, {
                    "features.log.maxLinesPerEntry": 1,
                    "features.log.prepopulateReadLimit": 0,
                    "features.log.maxRefreshRate": 10,
                    "cache.logPanel.size": 1000
                })

        # collapses duplicate log entries if false, showing only the most recent
        self.showDuplicates = self._config["features.log.showDuplicateEntries"]

        self.msgLog = []  # log entries, sorted by the timestamp
        self.loggedEvents = loggedEvents  # events we're listening to
        self.regexFilter = None  # filter for presented log events (no filtering if None)
        self.lastContentHeight = 0  # height of the rendered content when last drawn
        self.logFile = None  # file log messages are saved to (skipped if None)
        self.scroll = 0
        self._isPaused = False
        self._pauseBuffer = [
        ]  # location where messages are buffered if paused

        self._lastUpdate = -1  # time the content was last revised
        self._halt = False  # terminates thread if true
        self._cond = threading.Condition(
        )  # used for pausing/resuming the thread

        # restricts concurrent write access to attributes used to draw the display
        # and pausing:
        # msgLog, loggedEvents, regexFilter, scroll, _pauseBuffer
        self.valsLock = threading.RLock()

        # cached parameters (invalidated if arguments for them change)
        # last set of events we've drawn with
        self._lastLoggedEvents = []

        # _getTitle (args: loggedEvents, regexFilter pattern, width)
        self._titleCache = None
        self._titleArgs = (None, None, None)

        # fetches past tor events from log file, if available
        torEventBacklog = []
        if self._config["features.log.prepopulate"]:
            setRunlevels = list(
                set.intersection(set(self.loggedEvents), set(RUNLEVELS)))
            readLimit = self._config["features.log.prepopulateReadLimit"]
            addLimit = self._config["cache.logPanel.size"]
            torEventBacklog = getLogFileEntries(setRunlevels, readLimit,
                                                addLimit, self._config)

        # adds arm listener and fetches past events
        log.LOG_LOCK.acquire()
        try:
            armRunlevels = [log.DEBUG, log.INFO, log.NOTICE, log.WARN, log.ERR]
            log.addListeners(armRunlevels, self._registerArmEvent)

            # gets the set of arm events we're logging
            setRunlevels = []
            for i in range(len(armRunlevels)):
                if "ARM_" + RUNLEVELS[i] in self.loggedEvents:
                    setRunlevels.append(armRunlevels[i])

            armEventBacklog = []
            for level, msg, eventTime in log._getEntries(setRunlevels):
                runlevelStr = log.RUNLEVEL_STR[level]
                armEventEntry = LogEntry(eventTime, "ARM_" + runlevelStr, msg,
                                         RUNLEVEL_EVENT_COLOR[runlevelStr])
                armEventBacklog.insert(0, armEventEntry)

            # joins armEventBacklog and torEventBacklog chronologically into msgLog
            while armEventBacklog or torEventBacklog:
                if not armEventBacklog:
                    self.msgLog.append(torEventBacklog.pop(0))
                elif not torEventBacklog:
                    self.msgLog.append(armEventBacklog.pop(0))
                elif armEventBacklog[0].timestamp < torEventBacklog[
                        0].timestamp:
                    self.msgLog.append(torEventBacklog.pop(0))
                else:
                    self.msgLog.append(armEventBacklog.pop(0))
        finally:
            log.LOG_LOCK.release()

        # crops events that are either too old, or more numerous than the caching size
        self._trimEvents(self.msgLog)

        # leaving lastContentHeight as being too low causes initialization problems
        self.lastContentHeight = len(self.msgLog)

        # adds listeners for tor and torctl events
        conn = torTools.getConn()
        conn.addEventListener(TorEventObserver(self.registerEvent))
        conn.addTorCtlListener(self._registerTorCtlEvent)

        # opens log file if we'll be saving entries
        if self._config["features.logFile"]:
            logPath = self._config["features.logFile"]

            # make dir if the path doesn't already exist
            baseDir = os.path.dirname(logPath)
            if not os.path.exists(baseDir): os.makedirs(baseDir)

            try:
                self.logFile = open(logPath, "a")
                log.log(self._config["log.logPanel.logFileOpened"],
                        "arm %s opening log file (%s)" % (VERSION, logPath))
            except IOError, exc:
                log.log(
                    self._config["log.logPanel.logFileWriteFailed"],
                    "Unable to write to log file: %s" %
                    sysTools.getFileErrorMsg(exc))
                self.logFile = None