Beispiel #1
0
    def __init__(self):
        Observable.__init__(self)
        self.environ = Environment()
        self.mode = "gui"
        self.fix = False
        self.report = False
        self.undo = False
        self.runrule = None
        self.pcf = False
        self.pcs = False
        self.list = False
        if not self.safetycheck():
            self.logger.log(LogPriority.CRITICAL,
                            ['SafetyCheck',
                             'ERROR: Installation safety checks failed!'])
            self.logger.log(LogPriority.WARNING,
                            ['SafetyCheck',
                             'STONIX files may only be writable by the ' +
                             'root user!'])
            self.logger.log(LogPriority.WARNING,
                            ['SafetyCheck',
                             'Validate and correct permissions on all ' +
                             'STONIX files and try again.'])
            self.logger.log(LogPriority.WARNING,
                            ['SafetyCheck',
                             'STONIX will now exit.'])
            sys.exit(1)
        self.lockfile = '/var/run/stonix.pid'
        if(self.environ.get_test_mode()):
            self.setuptesting()
        if not self.mode == 'test':
            self.prog_args = ProgramArguments()
            self.processargs()
        self.config = Configuration(self.environ)
        self.numrulesrunning = 0
        self.numrulescomplete = 0
        self.currulename = ''
        self.currulenum = 0
        self.logger = LogDispatcher(self.environ)
        self.logger.log(LogPriority.DEBUG,
                        'Logging Started')
        self.tryacquirelock()

        if self.mode == 'gui':
            # This resets the UI to the command line if GUI was selected on the
            # command line and PyQt4 isn't present.
            if 'PyQt4' not in sys.modules and self.mode == 'gui':
                self.mode = 'cli'
                self.logger.log(LogPriority.ERROR,
                                'GUI Selected but PyQt4 not available. ' +
                                'Please install PyQt4 and dependencies for ' +
                                'GUI functionality.')
            else:
                app = QtGui.QApplication(sys.argv)
                splashart = os.path.join(self.environ.get_icon_path(),
                                         'StonixSplash.png')
                splashimage = QtGui.QPixmap(splashart)
                splash = QtGui.QSplashScreen(splashimage,
                                             QtCore.Qt.WindowStaysOnTopHint)
                splash.setMask(splashimage.mask())
                splash.show()
                app.processEvents()
        self.statechglogger = StateChgLogger(self.logger, self.environ)
        # NB We don't have a main event loop at this point so we call
        # the app.processEvents() again to make the splash screen show
        if self.mode == 'gui':
            app.processEvents()
        self.logger.log(LogPriority.DEBUG,
                        'State Logger Started')
        if not(self.mode == 'test'):
            # This resets the UI to the command line if GUI was selected on the
            # command line and PyQt4 isn't present.
            if 'PyQt4' not in sys.modules and self.mode == 'gui':
                self.mode = 'cli'
                self.logger.log(LogPriority.ERROR,
                                'GUI Selected but PyQt4 not available. ' +
                                'Please install PyQt4 and dependencies for ' +
                                'GUI functionality.')
        self.statechglogger = StateChgLogger(self.logger, self.environ)
        self.logger.log(LogPriority.DEBUG,
                        'State Logger Started')
        self.logger.log(LogPriority.DEBUG,
                        'Running in ' + self.mode)
        starttime = time.time()
        allrules = self.getrules(self.config, self.environ)
        etime = time.time() - starttime
        self.logger.log(LogPriority.DEBUG,
                        'Rules Processed in ' + str(etime))
        self.installedrules = self.findapplicable(allrules)
        etime = time.time() - starttime
        self.logger.log(LogPriority.DEBUG,
                        'Rules Applicable in ' + str(etime))
        self.numexecutingrules = len(self.installedrules)
        self.environ.setnumrules(self.numexecutingrules)
        self.logger.logRuleCount()
        self.logger.log(LogPriority.DEBUG,
                        str(self.numexecutingrules) + ' rules loaded')
        self.logger.log(LogPriority.DEBUG,
                        ['CurrentEUID', str(self.environ.geteuid())])
        self.logger.log(LogPriority.DEBUG,
                        ['OSFamily', self.environ.getosfamily()])
        self.logger.log(LogPriority.DEBUG,
                        ['OSType', self.environ.getostype()])
        self.logger.log(LogPriority.DEBUG,
                        ['OSVersion', self.environ.getosver()])
        self.list = self.prog_args.getList()
        if self.list:
            self.__listrules()

        if self.mode == 'cli':
            self.__clirun()
        elif self.mode == 'gui':
            myui = GUI(self, self.environ, self.logger)
            splash.finish(myui)
            sys.exit(app.exec_())
        elif self.mode == 'test':
            pass
Beispiel #2
0
class Controller(Observable):
    """This is the main worker object for stonix. It handles the
    stand up and tear down of the rest of the program.
    """
    def __init__(self):
        Observable.__init__(self)
        self.environ = Environment()
        self.mode = "gui"
        self.fix = False
        self.report = False
        self.undo = False
        self.runrule = None
        self.pcf = False
        self.pcs = False
        self.list = False
        if not self.safetycheck():
            self.logger.log(LogPriority.CRITICAL,
                            ['SafetyCheck',
                             'ERROR: Installation safety checks failed!'])
            self.logger.log(LogPriority.WARNING,
                            ['SafetyCheck',
                             'STONIX files may only be writable by the ' +
                             'root user!'])
            self.logger.log(LogPriority.WARNING,
                            ['SafetyCheck',
                             'Validate and correct permissions on all ' +
                             'STONIX files and try again.'])
            self.logger.log(LogPriority.WARNING,
                            ['SafetyCheck',
                             'STONIX will now exit.'])
            sys.exit(1)
        self.lockfile = '/var/run/stonix.pid'
        if(self.environ.get_test_mode()):
            self.setuptesting()
        if not self.mode == 'test':
            self.prog_args = ProgramArguments()
            self.processargs()
        self.config = Configuration(self.environ)
        self.numrulesrunning = 0
        self.numrulescomplete = 0
        self.currulename = ''
        self.currulenum = 0
        self.logger = LogDispatcher(self.environ)
        self.logger.log(LogPriority.DEBUG,
                        'Logging Started')
        self.tryacquirelock()

        if self.mode == 'gui':
            # This resets the UI to the command line if GUI was selected on the
            # command line and PyQt4 isn't present.
            if 'PyQt4' not in sys.modules and self.mode == 'gui':
                self.mode = 'cli'
                self.logger.log(LogPriority.ERROR,
                                'GUI Selected but PyQt4 not available. ' +
                                'Please install PyQt4 and dependencies for ' +
                                'GUI functionality.')
            else:
                app = QtGui.QApplication(sys.argv)
                splashart = os.path.join(self.environ.get_icon_path(),
                                         'StonixSplash.png')
                splashimage = QtGui.QPixmap(splashart)
                splash = QtGui.QSplashScreen(splashimage,
                                             QtCore.Qt.WindowStaysOnTopHint)
                splash.setMask(splashimage.mask())
                splash.show()
                app.processEvents()
        self.statechglogger = StateChgLogger(self.logger, self.environ)
        # NB We don't have a main event loop at this point so we call
        # the app.processEvents() again to make the splash screen show
        if self.mode == 'gui':
            app.processEvents()
        self.logger.log(LogPriority.DEBUG,
                        'State Logger Started')
        if not(self.mode == 'test'):
            # This resets the UI to the command line if GUI was selected on the
            # command line and PyQt4 isn't present.
            if 'PyQt4' not in sys.modules and self.mode == 'gui':
                self.mode = 'cli'
                self.logger.log(LogPriority.ERROR,
                                'GUI Selected but PyQt4 not available. ' +
                                'Please install PyQt4 and dependencies for ' +
                                'GUI functionality.')
        self.statechglogger = StateChgLogger(self.logger, self.environ)
        self.logger.log(LogPriority.DEBUG,
                        'State Logger Started')
        self.logger.log(LogPriority.DEBUG,
                        'Running in ' + self.mode)
        starttime = time.time()
        allrules = self.getrules(self.config, self.environ)
        etime = time.time() - starttime
        self.logger.log(LogPriority.DEBUG,
                        'Rules Processed in ' + str(etime))
        self.installedrules = self.findapplicable(allrules)
        etime = time.time() - starttime
        self.logger.log(LogPriority.DEBUG,
                        'Rules Applicable in ' + str(etime))
        self.numexecutingrules = len(self.installedrules)
        self.environ.setnumrules(self.numexecutingrules)
        self.logger.logRuleCount()
        self.logger.log(LogPriority.DEBUG,
                        str(self.numexecutingrules) + ' rules loaded')
        self.logger.log(LogPriority.DEBUG,
                        ['CurrentEUID', str(self.environ.geteuid())])
        self.logger.log(LogPriority.DEBUG,
                        ['OSFamily', self.environ.getosfamily()])
        self.logger.log(LogPriority.DEBUG,
                        ['OSType', self.environ.getostype()])
        self.logger.log(LogPriority.DEBUG,
                        ['OSVersion', self.environ.getosver()])
        self.list = self.prog_args.getList()
        if self.list:
            self.__listrules()

        if self.mode == 'cli':
            self.__clirun()
        elif self.mode == 'gui':
            myui = GUI(self, self.environ, self.logger)
            splash.finish(myui)
            sys.exit(app.exec_())
        elif self.mode == 'test':
            pass

    def getrules(self, config, environ):
        """
        Private method to process the stonix rules file to populate the rules.
        @return: List of instantiated rule objects

        @return: list : a list of instantiated rule classes
        @author: D. Kennel
        """
        instruleclasses = []
        validrulefiles = []
        initlist = ['__init__.py', '__init__.pyc', '__init__.pyo']

        stonixPath = self.environ.get_resources_path()
        self.logger.log(LogPriority.DEBUG,
                        ['STONIX Path:', str(stonixPath)])
        rulesPath = self.environ.get_rules_path()
        self.logger.log(LogPriority.DEBUG,
                        ['Rules Path:', str(rulesPath)])

        sys.path.append(stonixPath)
        sys.path.append(rulesPath)

        for path in sys.path:
            self.logger.log(LogPriority.DEBUG,
                            ['Sys Path Element:', str(path)])

        rulefiles = os.listdir(str(rulesPath))
        # print str(rulefiles)

        for rfile in rulefiles:
            if rfile in initlist:
                continue
            else:
                validrulefiles.append(rfile)
        self.logger.log(LogPriority.DEBUG,
                        ['validrulefiles:', str(validrulefiles)])

        # This is a list comprehension to build a list of module names to
        # import based on the names of valid rule files from stonix/rules
        # destination list = [ *transform* *source* *filter* ]
        modulenames = [mod.replace('.py', '') for mod in validrulefiles
                        if re.search("\.py$", mod)]
        self.logger.log(LogPriority.DEBUG,
                        ['Module names:', str(modulenames)])

        # The output of this section is a list of valid, fully qualified,
        # rule class names.
        classnames = []
        for module in modulenames:
            module = module.split("/")[-1]
            # print module
            classname = 'stonix_resources.rules.' + module + '.' + module
            classnames.append(classname)

        # This is odd and requires detailed comments. This block imports the
        # modules then recurses down, instantiates the main rule class and
        # appends the class to a list.
        for cls in classnames:
            starttime = time.time()
            parts = cls.split(".")
            # the module is the class less the last element
            module = ".".join(parts[:-1])
            # Using the __import__ built in function to import the module
            # since our names are only known at runtime.
            self.logger.log(LogPriority.DEBUG,
                            'Attempting to load: ' + module)
            try:
                mod = __import__(module)
            except Exception:
                trace = traceback.format_exc()
                self.logger.log(LogPriority.ERROR,
                                "Error importing rule: " + trace)
                continue
            # Recurse down the class name until we get a reference to the class
            # itself. Then we instantiate using the reference.
            for component in parts[1:]:
                try:
                    mod = getattr(mod, component)
                except Exception:
                    trace = traceback.format_exc()
                    self.logger.log(LogPriority.ERROR,
                                    "Error finding rule class reference: "
                                    + trace)
                    continue
            try:
                clinst = mod(config, environ, self.logger, self.statechglogger)
                instruleclasses.append(clinst)
                etime = time.time() - starttime
                self.logger.log(LogPriority.DEBUG,
                                'load time: ' + str(etime))
            except (KeyboardInterrupt, SystemExit):
            # User initiated exit
                raise
            except Exception:
                trace = traceback.format_exc()
                self.logger.log(LogPriority.ERROR,
                                "Error instantiating rule: " + trace)
                continue
        # print instruleclasses
        return instruleclasses

    def findapplicable(self, rules):
        """
        This method checks each rule to see if it is applicable on the current
        platform on which stonix is running. A list of applicable rule objects
        is returned.

        @param rules: List of instantiated rule objects
        @return: List of instantiated rule objects
        @author: D. Kennel
        """
        applicablerules = []
        for rule in rules:
            try:
                if rule.isapplicable():
                    if not self.environ.geteuid() == 0 and rule.getisrootrequired():
                        continue
                    self.logger.log(LogPriority.DEBUG,
                                    'Adding ' + rule.getrulename() +
                                    ' to run list.')
                    applicablerules.append(rule)
            except Exception:
                trace = traceback.format_exc()
                self.logger.log(LogPriority.ERROR,
                                "Error determining rule applicability: "
                                + trace)
        return applicablerules

    def getallrulesdata(self):
        """
        This method returns a dictionary of lists containing the data for all
        installed rules. Returned properties include; rule name, rule number,
        and the rule help text. The dictionary is keyed by rule number.

        @return: Dictionary of lists
        @author: D. Kennel
        """
        rulesdata = {}
        for rule in self.installedrules:
            rulenum = rule.getrulenum()
            rulename = rule.getrulename()
            ruletxt = rule.gethelptext()
            ruledetails = []
            ruledetails.append(rulename)
            ruledetails.append(ruletxt)
            rulesdata[rulenum] = ruledetails
        return rulesdata

    def getrulenumbyname(self, name):
        """
        This method takes a name associated with a rule as an argument and
        translates it into a number associated with a rule class. Acceptable
        rule names are the section headers from the stonix_resources.conf.
        These section headers are generated from the rulename property that
        each rule has.

        @param string: name of the rule to fetch a number for.
        @return: int : number for the rule matching the passed name 0 if no
        match is found.
        @author: D. Kennel
        """
        rulenum = 0
        for rule in self.installedrules:
            rulename = rule.getrulename()
            if rulename == name:
                rulenum = rule.getrulenum()
        return rulenum

    def getrulenamebynum(self, rulenum):
        """
        This method takes a number associated with a rule as an argument and
        translates it into a name associated with a rule class. Acceptable
        rule number are integers and are found in rule implementations.

        @param int: number of the rule to fetch a name for.
        @return: string : name for the rule matching the passed number. None if
        no match is found.
        @author: D. Kennel
        """
        rulename = None
        for rule in self.installedrules:
            ruleid = rule.getrulenum()
            if rulenum == ruleid:
                rulename = rule.getrulename()
        return rulename

    def hardensystem(self):
        """
        Call all rules in fix(harden) mode

        @return void :
        @author D. Kennel
        """
        self.numrulesrunning = self.numexecutingrules
        self.numrulescomplete = 0
        for rule in self.installedrules:
            self.currulenum = rule.getrulenum()
            self.currulename = rule.getrulename()
            try:
                starttime = time.time()
                rule.report()
                if not rule.getrulesuccess():
                    self.logger.log(LogPriority.ERROR,
                                    [rule.getrulename(),
                                     rule.getdetailedresults()])
                elif not rule.iscompliant():
                    rule.fix()
                    if rule.getrulesuccess():
                        rule.report()
                        if not rule.getrulesuccess():
                            self.logger.log(LogPriority.ERROR,
                                            [rule.getrulename(),
                                             rule.getdetailedresults()])
                        elif not rule.iscompliant():
                            self.logger.log(LogPriority.WARNING,
                                            [rule.getrulename(),
                                            rule.getdetailedresults()])
                        else:
                            self.logger.log(LogPriority.INFO,
                                            [rule.getrulename(),
                                            rule.getdetailedresults()])
                else:
                    self.logger.log(LogPriority.INFO,
                                    [rule.getrulename(),
                                    rule.getdetailedresults()])
                etime = time.time() - starttime
                self.logger.log(LogPriority.DEBUG,
                                [rule.getrulename(),
                                'Elapsed Time: ' + str(etime)])
            except (KeyboardInterrupt, SystemExit):
            # User initiated exit
                raise
            except Exception:
                trace = traceback.format_exc()
                self.logger.log(LogPriority.ERROR, [rule.getrulename(),
                                "Controller caught rule death: "
                                + trace])
            self.numrulescomplete = self.numrulescomplete + 1
            self.set_dirty()
            self.notify_check()

    def auditsystem(self):
        """
        Call all rules in audit(report) mode

        @return void :
        @author D. Kennel
        """
        self.numrulesrunning = self.numexecutingrules
        self.numrulescomplete = 0
        for rule in self.installedrules:
            self.currulenum = rule.getrulenum()
            self.currulename = rule.getrulename()
            try:
                starttime = time.time()
                rule.report()
                etime = time.time() - starttime
                self.logger.log(LogPriority.DEBUG,
                                [rule.getrulename(),
                                'Elapsed Time: ' + str(etime)])
            except (KeyboardInterrupt, SystemExit):
                # User initiated exit
                raise
            except Exception:
                trace = traceback.format_exc()
                self.logger.log(LogPriority.ERROR, [rule.getrulename(),
                                "Controller caught rule death: "
                                + trace])
            self.numrulescomplete = self.numrulescomplete + 1
            if not rule.getrulesuccess():
                self.logger.log(LogPriority.ERROR,
                                [rule.getrulename(),
                                rule.getdetailedresults()])
            if not rule.iscompliant():
                self.logger.log(LogPriority.WARNING,
                                [rule.getrulename(),
                                rule.getdetailedresults()])
            else:
                self.logger.log(LogPriority.INFO,
                                [rule.getrulename(),
                                rule.getdetailedresults()])
            self.set_dirty()
            self.notify_check()

    def runruleharden(self, ruleid):
        """
        Run a single rule in fix(harden) mode

        @param int ruleid :
        @return void :
        @author D. Kennel
        """
        self.numrulesrunning = 1
        self.numrulescomplete = 0
        self.logger.log(LogPriority.DEBUG, ['RunRuleHarden',
                         'Attempting to run ' + str(ruleid)])

        for rule in self.installedrules:
            if ruleid == rule.getrulenum():
                if rule.getisrootrequired() and self.environ.geteuid() != 0:
                    self.numrulescomplete = self.numrulescomplete + 1
                    message = "Could not run rule: insufficient privilege level"
                    self.logger.log(LogPriority.ERROR,
                                    [rule.getrulename(), message])
                else:
                    starttime = time.time()
                    try:
                        rule.report()
                    except (KeyboardInterrupt, SystemExit):
                        # User initiated exit
                        raise
                    except Exception:
                        trace = traceback.format_exc()
                        self.logger.log(LogPriority.ERROR, [rule.getrulename(),
                                        "Controller caught rule death: "
                                        + trace])
                    if not rule.getrulesuccess():
                        self.logger.log(LogPriority.ERROR,
                                        [rule.getrulename(),
                                        rule.getdetailedresults()])
                        self.logger.log(LogPriority.INFO,
                                        [rule.getrulename(),
                                         'Rule failed: Rule config in unknown state. Skipping rule'])
                        self.numrulescomplete = self.numrulescomplete + 1
                    elif not rule.iscompliant():
                        try:
                            rule.fix()
                        except (KeyboardInterrupt, SystemExit):
                            # User initiated exit
                            raise
                        except Exception:
                            trace = traceback.format_exc()
                            self.logger.log(LogPriority.ERROR,
                                            [rule.getrulename(),
                                            "Controller caught rule death: "
                                            + trace])
                        if not rule.getrulesuccess():
                            self.logger.log(LogPriority.ERROR,
                                            [rule.getrulename(),
                                             rule.getdetailedresults()])
                        try:
                            rule.report()
                        except (KeyboardInterrupt, SystemExit):
                            # User initiated exit
                            raise
                        except Exception:
                            trace = traceback.format_exc()
                            self.logger.log(LogPriority.ERROR,
                                            [rule.getrulename(),
                                             "Controller caught rule death: "
                                            + trace])
                        if not rule.iscompliant():
                            self.logger.log(LogPriority.WARNING,
                                            [rule.getrulename(),
                                            rule.getdetailedresults()])
                        else:
                            self.logger.log(LogPriority.INFO,
                                            [rule.getrulename(),
                                            rule.getdetailedresults()])
                    self.numrulescomplete = self.numrulescomplete + 1
                    etime = time.time() - starttime
                    self.logger.log(LogPriority.DEBUG,
                                    [rule.getrulename(),
                                     'Elapsed Time: ' + str(etime)])
                    self.set_dirty()
                    self.notify_check()
        if self.numrulescomplete == 0:
            message = "Could not find rule! Searched for ruleid =" \
            + str(ruleid)
            self.logger.log(LogPriority.ERROR,
                            message)

    def runruleaudit(self, ruleid):
        """
        Run a single rule in audit(report) mode

        @param int ruleid :
        @return void :
        @author D. Kennel
        """
        message = "Controller:runruleaudit: Entering with rule id " + \
        str(ruleid)
        self.logger.log(LogPriority.DEBUG, message)
        self.numrulesrunning = 1
        self.numrulescomplete = 0
        for rule in self.installedrules:
            if ruleid == rule.getrulenum():
                message = "Controller:runruleaudit: Matched ruleid"
                self.logger.log(LogPriority.DEBUG, message)
                if rule.getisrootrequired() and self.environ.geteuid() != 0:
                    self.numrulescomplete = self.numrulescomplete + 1
                    message = "Could not run rule: insufficient privilege level"
                    self.logger.log(LogPriority.ERROR,
                                    [rule.getrulename(), message])
                else:
                    starttime = time.time()
                    try:
                        rule.report()
                    except (KeyboardInterrupt, SystemExit):
                        # User initiated exit
                        raise
                    except Exception:
                        trace = traceback.format_exc()
                        self.logger.log(LogPriority.ERROR,
                                        [rule.getrulename(),
                                        "Controller caught rule death: "
                                        + trace])
                    self.numrulescomplete = self.numrulescomplete + 1
                    if not rule.getrulesuccess():
                        self.logger.log(LogPriority.ERROR,
                                        [rule.getrulename(),
                                        rule.getdetailedresults()])
                    if not rule.iscompliant():
                        self.logger.log(LogPriority.WARNING,
                                        [rule.getrulename(),
                                        rule.getdetailedresults()])
                    else:
                        self.logger.log(LogPriority.INFO,
                                        [rule.getrulename(),
                                        rule.getdetailedresults()])
                    etime = time.time() - starttime
                    self.logger.log(LogPriority.DEBUG,
                                    [rule.getrulename(),
                                     'Elapsed Time: ' + str(etime)])
                    self.set_dirty()
                    self.notify_check()

    def undochangessystem(self):
        """
        Undo all changes to the system.

        @return void :
        @author D. Kennel
        """
        self.numrulesrunning = self.numexecutingrules
        self.numrulescomplete = 0
        for rule in self.installedrules:
            self.currulenum = rule.getrulenum()
            self.currulename = rule.getrulename()
            try:
                rule.undo()
            except (KeyboardInterrupt, SystemExit):
                # User initiated exit
                raise
            except Exception:
                trace = traceback.format_exc()
                self.logger.log(LogPriority.ERROR,
                                [rule.getrulename(),
                                "Controller caught rule death: "
                                + trace])
            self.numrulescomplete = self.numrulescomplete + 1
            if not rule.getrulesuccess():
                self.logger.log(LogPriority.ERROR,
                                [rule.getrulename(),
                                rule.getdetailedresults()])
            else:
                self.logger.log(LogPriority.INFO,
                                [rule.getrulename(),
                                 rule.getdetailedresults()])
            self.set_dirty()
            self.notify_check()

    def undorule(self, ruleid):
        """
        Undo the changes from a single rule. Expects the integer rule number
        as the ruleid

        @param int ruleid :
        @return void :
        @author D. Kennel
        """
        self.numrulesrunning = 1
        self.numrulescomplete = 0
        for rule in self.installedrules:
            if ruleid == rule.getrulenum():
                if rule.getisrootrequired() and self.environ.geteuid() != 0:
                    self.numrulescomplete = self.numrulescomplete + 1
                    message = "Could not run rule: insufficient privilege level"
                    self.logger.log(LogPriority.ERROR,
                                    [rule.getrulename(), message])
                else:
                    try:
                        rule.undo()
                    except (KeyboardInterrupt, SystemExit):
                        # User initiated exit
                        raise
                    except Exception:
                        trace = traceback.format_exc()
                        self.logger.log(LogPriority.ERROR,
                                        [rule.getrulename(),
                                        "Controller caught rule death: "
                                        + trace])
                    self.numrulescomplete = self.numrulescomplete + 1
                    if not rule.getrulesuccess():
                        self.logger.log(LogPriority.ERROR,
                                        [rule.getrulename(),
                                        rule.getdetailedresults()])
                    else:
                        self.logger.log(LogPriority.INFO,
                                        [rule.getrulename(),
                                        rule.getdetailedresults()])
                    self.set_dirty()
                    self.notify_check()

    def getrulehelp(self, ruleid):
        """
        Return rule help information.

        @param int ruleid : int (identifier) of rule to get help text for.
        @return string :
        @author D. Kennel
        """
        helptxt = []
        for rule in self.installedrules:
            if ruleid == rule.getrulenum():
                helptxt = rule.gethelptext()
        return helptxt

    def updatedbs(self):
        """
        Update all databases held by database rules in stonix_resources.

        @return void :
        @author D. Kennel
        """
        numdbrules = 0
        self.numrulescomplete = 0
        for rule in self.installedrules:
            if rule.isdatabaserule():
                numdbrules = numdbrules + 1
        self.numrulesrunning = numdbrules
        for rule in self.installedrules:
            if rule.isdatabaserule():
                self.currulenum = rule.getrulenum()
                self.currulename = rule.getrulename()
                try:
                    rule.fix()
                except (KeyboardInterrupt, SystemExit):
                    # User initiated exit
                    raise
                except Exception:
                    trace = traceback.format_exc()
                    self.logger.log(LogPriority.ERROR,
                                    [rule.getrulename(),
                                    "Controller caught rule death: "
                                    + trace])
                self.numrulescomplete = self.numrulescomplete + 1
                self.set_dirty()
                self.notify_check()

    def getconfigoptions(self):
        """
        This method retrieves the configitems for all rules and returns a dict
        of lists where the keys are rule names and the lists contain the rule
        text and a list of configitem objects for that rule.

        @return dict :
        @author D. Kennel
        """
        configdict = {}
        for rule in self.installedrules:
            rulename = rule.getrulename()
            ruledata = []
            ruledata.append(rule.gethelptext())
            for configitem in rule.getconfigitems():
                ruledata.append(configitem)
            configdict[rulename] = ruledata

        return configdict

    def getruleconfigoptions(self, ruleid):
        """
        This method returns the configurationitem object instances associated
        with a rule. We expect to be passed the integer rule number to id the
        rule.

        @param int ruleid : Integer rule number
        @return list : list of configurationitem objects
        @author D. Kennel
        """
        cilist = []
        for rule in self.installedrules:
            if ruleid == rule.getrulenum():
                cilist = rule.getconfigitems()
        return cilist

    def regenerateconfig(self, simpleconf):
        """
        This method will write the stonix configuration file with the current
        configuration data. If simpleconf is True then we only write changed
        rules and rules that are marked as being in the simple config.

        The configuration file object actually does most of the work here.

        @param bool simpleconf : Whether or not we are generating a simple
        configuration file or not.
        @return  : void
        @author D. Kennel
        """
        currdata = self.getconfigoptions()
        self.config.writeconfig(simpleconf, currdata)

    def validateconfig(self):
        """
        FIXME - unimplemented method, is this still required/desirable at this
        level?

        @return bool :
        @author
        """
        pass

    def getcurrentrule(self):
        """
        This method returns the rule name for the currently executing rule.
        This method only returns valid data when called while the whole rule
        stack is running.

        @return string : rulename
        @author D. Kennel
        """
        return self.currulename

    def getrulecompstatus(self, ruleid):
        """
        This method returns the compliance status for the named rule. This info
        is only valid after the rule has had the report or fix methods called.

        @param int: ruleid
        @return: bool
        @author: D. Kennel
        """
        compliant = False
        for rule in self.installedrules:
            if ruleid == rule.getrulenum():
                compliant = rule.iscompliant()
        return compliant

    def getruledetailedresults(self, ruleid):
        """
        This method returns the detailed results from the rule with a given
        rule id. Returned data will be a string.

        @param int: ruleid
        @return: string
        @author: D. Kennel
        """
        detailedresults = []
        for rule in self.installedrules:
            if ruleid == rule.getrulenum():
                detailedresults = rule.getdetailedresults()
        return detailedresults

    def getcompletionpercentage(self):
        """
        This method returns the percentage of items on the to-do list
        completed. This only returns valid data when called while the whole
        rule stack is running.

        @return int : range 0 - 100
        @author D. Kennel
        """
        total = float(self.numrulesrunning)
        curr = float(self.numrulescomplete)
        if self.environ.getdebugmode():
            print "Controller:getcompletionpercentage: Total: " + str(total)
            print "Controller:getcompletionpercentage: Current: " + str(curr)
        if curr == 0:
            percent = 0
        else:
            percent = int((curr / total) * 100)
        if self.environ.getdebugmode():
            print "Controller:getcompletionpercentage: Percent: " + str(percent)
        return percent

    def displaylastrun(self):
        """
        Returns the contents of the log file by way of the logger object.

        @return string :
        @author
        """
        return self.logger.displaylastrun()

    def updatestatus(self, callingobject):
        """
        WARNING! FIX ME! This was intended to be a part of the comms between
        the controller and the view. I'm not sure this method still makes
        sense.

        @param object_ callingobject :
        @return  :
        @author
        """
        pass

    def tryacquirelock(self):
        """
        Try to set a lock file at /var/run/stonix_resources.pid. If the lock
        file already exists check to see if a stonix process with that PID is
        already running. If so exit with error else re-create lock file with
        our PID.

        @return: void
        @author: D. Kennel
        """
        lockmessage = """
!WARNING! Another copy of STONIX appears to be running!
Running more than one copy of STONIX at a time may result in
unintended behavior! If STONIX is not running remove the file
/var/run/stonix.pid and re-launch STONIX.
ABORTING EXECUTION!"""
        if not self.environ.geteuid() == 0:
            # only grab a lock file for privileged use.
            return
        if os.path.exists(self.lockfile):
            rw_lockfile = open(self.lockfile, 'r+')
            lockpid = rw_lockfile.readline()
            lockpid = lockpid.strip()
            self.logger.log(LogPriority.DEBUG,
                            ['TryAcquireLock',
                             'Found lock for PID: ' + lockpid])
            try:
                if int(lockpid) == os.getpid():
                    # PID is the same, reuse PID file
                    return
            except (TypeError, ValueError):
                self.logger.log(LogPriority.DEBUG,
                                ['TryAcquireLock',
                                 'Could not coerce PID to INT: ' + lockpid])
            command = None
            if self.environ.getosfamily() == 'freebsd':
                command = '/bin/ps -aux'
            elif self.environ.getosfamily() == 'solaris':
                command = '/usr/bin/ps -ef'
            else:
                command = '/bin/ps -ef'
            self.logger.log(LogPriority.DEBUG,
                            ['TryAcquireLock',
                             'PS command is: ' + str(command)])
            if not type(command) is None:
                pscom = subprocess.Popen(command, stdout=subprocess.PIPE,
                                         stderr=subprocess.PIPE,
                                         shell=True, close_fds=True)
                psout = pscom.stdout.readlines()
                for line in psout:
                    if re.search('stonix', line):
                        splits = line.split()
                        pspid = splits[1]
                        self.logger.log(LogPriority.DEBUG,
                                        ['TryAcquireLock',
                                         'Checking PIDs: ' + lockpid + ' ' + \
                                         pspid])
                        if pspid == lockpid:
                            self.logger.log(LogPriority.DEBUG,
                                            ['TryAcquireLock',
                                             'Matched PIDs: ' + lockpid + ' ' \
                                             + pspid])
                            self.logger.log(LogPriority.CRITICAL,
                                            ['TryAcquireLock', lockmessage])
                            rw_lockfile.close()
                            sys.exit(2)
            self.logger.log(LogPriority.DEBUG,
                            ['TryAcquireLock',
                             'Truncating existing Lockfile'])
            rw_lockfile.truncate(0)
            rw_lockfile.write(str(os.getpid()))
            rw_lockfile.close()
        else:
            self.logger.log(LogPriority.DEBUG,
                            ['TryAcquireLock',
                             'Creating new Lockfile'])
            rw_lockfile = open(self.lockfile, 'w')
            rw_lockfile.write(str(os.getpid()))
            rw_lockfile.close()

    def safetycheck(self):
        """
        Check that the installation of STONIX is safe from a security
        perspective. All files must only be writable by root.

        @author: dkennel
        @return: bool True if install passes checks
        """
        safe = True
        return safe

    def releaselock(self):
        """
        Cleans up the stonix lock file in the event of normal program
        exit.

        @return: void
        @author: D. Kennel
        """

        if os.path.exists(self.lockfile):
            try:
                os.remove(self.lockfile)
            except:
                # the lock release can cause errors when run w/o privilege
                # and a lock file has been left behind by a privileged run.
                pass

    def processargs(self):
        """
        This method calls the prog_args instance to process the command line
        args and then jumps to the appropriate execution mode.

        @author: R. Nielsen, D. Kennel
        @return: void
        """
        self.environ.setverbosemode(self.prog_args.get_verbose())
        self.environ.setdebugmode(self.prog_args.get_debug())
        self.fix = self.prog_args.get_fix()
        self.report = self.prog_args.get_report()
        self.undo = self.prog_args.get_rollback()
        self.environ.setinstallmode(self.prog_args.get_install())
        self.pcf = self.prog_args.getPrintConfigFull()
        self.pcs = self.prog_args.getPrintConfigSimple()

        if self.prog_args.get_update():
            # update(debug)
            pass

        if self.prog_args.get_rollback():
            # rollback()
            pass

        if not re.match("^\s*$", self.prog_args.get_module()):
            self.runrule = self.prog_args.get_module()

        if self.prog_args.get_gui():
            self.mode = 'gui'
            # print "Launching UI"
            # uiinstance = view.View(uimode)

        if self.prog_args.get_cli():
            self.mode = 'cli'

    def setuptesting(self):
        """
        this method is called when the environment object determins that the
        controller is in test mode - e.g. running from a unittest.

        @author: ekkehard j. Koch
        @return: void
        """
        self.environ.setverbosemode(True)
        self.environ.setdebugmode(True)
        self.fix = False
        self.report = False
        self.undo = False
        self.environ.setinstallmode(False)
        self.runrule = ""
        self.mode = 'test'

    def __listrules(self):
        """
        Private method that prints the list of currently installed and
        applicable rules. This is a part of the STONIX CLI user interface and
        is needed for users to be able to list rules available to run in module
        mode. The program will exit after this method is complete.

        @author: D. Kennel
        @return: void
        """
        rulelist = []
        for rule in self.installedrules:
            rulenum = rule.getrulenum()
            rulename = rule.getrulename()
            rulestring = rulename + ' (' + str(rulenum) + ')'
            rulelist.append(rulestring)
        rulelist.sort(key=str.lower)
        print "STONIX rules for this platform:"
        for rule in rulelist:
            print rule
        try:
            self.logger.closereports()
        except:
            pass
        self.releaselock()

    def __clirun(self):
        """
        This private method performs a cli run based on the passed flags.

        @author: D. Kennel
        @return: void
        """
        self.logger.log(LogPriority.DEBUG,
                        ['Controller.__clirun',
                         ' __clirun called'])
        myui = Cli(self.environ)
        # self.logger.register_listener(myui)
        self.register_listener(myui)
        if not self.runrule:
            self.logger.log(LogPriority.DEBUG,
                            'Entering full system run')
            if self.fix:
                self.logger.log(LogPriority.DEBUG,
                                'Mode is Fix')
                self.hardensystem()
                self.logger.closereports()
            if self.report:
                self.logger.log(LogPriority.DEBUG,
                                'Mode is Report')
                self.auditsystem()
                self.logger.postreport()
            if self.undo:
                self.logger.log(LogPriority.DEBUG,
                                'Mode is Undo')
                self.undochangessystem()
                self.logger.closereports()
            if self.pcf:
                self.logger.log(LogPriority.DEBUG,
                                'Generating Full config file')
                self.regenerateconfig(False)
                self.logger.closereports()
            if self.pcs:
                self.logger.log(LogPriority.DEBUG,
                                'Generating Simple config file')
                self.regenerateconfig(True)
                self.logger.closereports()
            if not self.fix and not self.report and not self.undo and \
            not self.pcf and not self.pcs:
                self.logger.log(LogPriority.INFO,
                                'No action specified. Please check command syntax')
                self.logger.closereports()

        else:
            self.logger.log(LogPriority.DEBUG,
                           'Entering single rule run ' + self.runrule)
            ruleid = self.getrulenumbyname(self.runrule)
            self.logger.log(LogPriority.DEBUG,
                           'Rule ID number ' + str(ruleid))
            if self.fix:
                self.logger.log(LogPriority.DEBUG,
                                'Mode is Fix')
                self.runruleharden(ruleid)
                self.logger.closereports()
            if self.report:
                self.logger.log(LogPriority.DEBUG,
                                'Mode is Report')
                self.runruleaudit(ruleid)
                self.logger.closereports()
            if self.undo:
                self.logger.log(LogPriority.DEBUG,
                                'Mode is Undo')
                self.undorule(ruleid)
                self.logger.closereports()
            if not self.fix and not self.report and not self.undo:
                self.logger.log(LogPriority.INFO,
                                'No action specified. Please pass the -r, -f, or -u flag')
                self.logger.closereports()
        self.releaselock()