class zzzTestConfigureRemoteManagement(RuleTest):

    def setUp(self):

        RuleTest.setUp(self)
        self.rule = ConfigureRemoteManagement(self.config,
                                self.environ,
                                self.logdispatch,
                                self.statechglogger)
        self.cmdhelper = CommandHelper(self.logdispatch)
        self.rulename = self.rule.rulename
        self.rulenumber = self.rule.rulenumber

    def tearDown(self):
        pass

    def runTest(self):
        self.simpleRuleTest()

    def setConditionsForRule(self):
        '''
        Configure system for the unit test
        @param self: essential if you override this definition
        @return: boolean - If successful True; If failure False
        @author: ekkehard j. koch
        '''
        success = True
        setupdict = {"ARD_AllLocalUsers": "True",
                     "ScreenSharingReqPermEnabled": "False",
                     "VNCLegacyConnectionsEnabled": "True",
                     "LoadRemoteManagementMenuExtra": "False"}

        for key in setupdict:
            self.cmdhelper.executeCommand("defaults write /Library/Preferences/com.apple.RemoteManagement " + key + " -bool " + setupdict[key])
            errorout = self.cmdhelper.getError()
            if errorout:
                success = False
        return success

    def checkReportForRule(self, pCompliance, pRuleSuccess):
        '''
        check on whether report was correct
        @param self: essential if you override this definition
        @param pCompliance: the self.iscompliant value of rule
        @param pRuleSuccess: did report run successfully
        @return: boolean - If successful True; If failure False
        @author: ekkehard j. koch
        '''
        self.logdispatch.log(LogPriority.DEBUG, "pCompliance = " + \
                             str(pCompliance) + ".")
        self.logdispatch.log(LogPriority.DEBUG, "pRuleSuccess = " + \
                             str(pRuleSuccess) + ".")
        success = True
        return success

    def checkFixForRule(self, pRuleSuccess):
        '''
        check on whether fix was correct
        @param self: essential if you override this definition
        @param pRuleSuccess: did report run successfully
        @return: boolean - If successful True; If failure False
        @author: ekkehard j. koch
        '''
        self.logdispatch.log(LogPriority.DEBUG, "pRuleSuccess = " + \
                             str(pRuleSuccess) + ".")
        success = True
        return success

    def checkUndoForRule(self, pRuleSuccess):
        '''
        check on whether undo was correct
        @param self: essential if you override this definition
        @param pRuleSuccess: did report run successfully
        @return: boolean - If successful True; If failure False
        @author: ekkehard j. koch
        '''
        self.logdispatch.log(LogPriority.DEBUG, "pRuleSuccess = " + \
                             str(pRuleSuccess) + ".")
        success = True
        return success
Exemple #2
0
class SystemIntegrityProtectionObject():
    '''the SystemIntegrityProtectionObject gets System Integrity Protection(SIP)
    data from the local system
    @author: ekkehard


    '''
    def __init__(self, logdispatcher):
        '''
        initialize lanlMacInfo
        @author: ekkehard
        '''
        # Make sure we have the full path for all commands
        self.logdispatch = logdispatcher
        self.ch = CommandHelper(self.logdispatch)
        self.csrutil = "/usr/bin/csrutil"
        self.sw_vers = "/usr/bin/sw_vers"
        self.osx_major_version = 0
        self.osx_minor_version = 0
        self.osx_version_string = ""
        self.initializeOSXVersionBoolean = False
        self.initializeSIPStatusBoolean = False
        self.sipstatus = ""
        # Reset messages
        self.messageReset()

    def getSIPStatus(self):
        '''get the current System Integrity Protection (SIP) status
        @author: ekkehard


        :returns: dictionary entry

        '''
        osx_version = self.initializeOSXVersion()
        sipstatus = self.initializeSIPStatus()
        msg = "SIP status is " + str(
            sipstatus) + ". OS X Version string is " + str(osx_version) + "."
        self.logdispatch.log(LogPriority.DEBUG, msg)
        return self.sipstatus

    def initializeOSXVersion(self, forceInitializtion=False):
        '''initialize OS X version info
        @author: ekkehard

        :param forceInitializtion:  (Default value = False)
        :returns: boolean - True

        '''
        success = True
        if forceInitializtion:
            self.initializeOSXVersionBoolean = False
        if not self.initializeOSXVersionBoolean:
            try:
                self.initializeOSXVersionBoolean = True
                # Get the major version of OS X
                command = self.sw_vers + " -productVersion | awk -F. '{print $1}'"
                self.ch.executeCommand(command)
                errorcode = self.ch.getError()
                osxmajorversion = self.ch.getOutputString().strip()
                self.osx_major_version = int(osxmajorversion)
                msg = "(" + str(errorcode) + ") OS X Major Version is " + str(
                    self.osx_major_version)
                self.logdispatch.log(LogPriority.DEBUG, msg)
                # Get the minor version of OS X
                command = self.sw_vers + " -productVersion | awk -F. '{print $2}'"
                self.ch.executeCommand(command)
                errorcode = self.ch.getError()
                osxminorversion = self.ch.getOutputString().strip()
                self.osx_minor_version = int(osxminorversion)
                msg = "(" + str(errorcode) + ") OS X Minor Version is " + str(
                    self.osx_minor_version)
                self.logdispatch.log(LogPriority.DEBUG, msg)
                # Get full version string
                command = self.sw_vers + " -productVersion"
                self.ch.executeCommand(command)
                errorcode = self.ch.getError()
                self.osx_version_string = self.ch.getOutputString().strip()
                self.osx_minor_version = int(osxminorversion)
                msg = "(" + str(errorcode) + ") OS X Version string is " + str(
                    self.osx_minor_version)
                self.logdispatch.log(LogPriority.DEBUG, msg)
            except (KeyboardInterrupt, SystemExit):
                raise
            except Exception:
                msg = traceback.format_exc()
                self.logdispatch.log(LogPriority.ERROR, msg)
                success = False
        return success

    def initializeSIPStatus(self, forceInitializtion=False):
        '''Initialize SIP Status
        @author: ekkehard

        :param forceInitializtion:  (Default value = False)
        :returns: boolean - True

        '''
        success = True
        if forceInitializtion:
            self.initializeSIPStatusBoolean = False
        if not self.initializeSIPStatusBoolean:
            try:
                self.initializeSIPStatusBoolean = True
                if self.osx_major_version < 10:
                    self.sipstatus = "Not Applicable"
                elif self.osx_minor_version < 11:
                    command = self.sw_vers + " -productVersion"
                    self.ch.executeCommand(command)
                    errorcode = self.ch.getError()
                    self.sipstatus = "Not Applicable For " + self.osx_version_string
                    msg = "(" + str(errorcode) + ") SIP status is " + str(
                        self.sipstatus)
                    self.logdispatch.log(LogPriority.DEBUG, msg)
                elif os.path.exists(self.csrutil):
                    command = self.csrutil + " status | awk '/status/ {print $5}' | sed 's/\.$//'"
                    self.ch.executeCommand(command)
                    errorcode = self.ch.getError()
                    sipstatus = self.ch.getOutputString().strip()
                    if sipstatus == "disabled":
                        self.sipstatus = "Disabled"
                    elif sipstatus == "enabled":
                        self.sipstatus = "Enabled"
                    else:
                        self.sipstatus = "Not Applicable - " + str(
                            sipstatus) + " not a know status value."
                    msg = "(" + str(errorcode) + ") SIP status is " + str(
                        self.sipstatus)
                    self.logdispatch.log(LogPriority.DEBUG, msg)
                else:
                    self.sipstatus = "Not Applicable - " + str(
                        self.csrutil) + " not available!"
                msg = "The System Ingegrity Protection status is " + str(
                    self.sipstatus)
                self.logdispatch.log(LogPriority.DEBUG, msg)
            except (KeyboardInterrupt, SystemExit):
                raise
            except Exception:
                msg = traceback.format_exc()
                self.logdispatch.log(LogPriority.ERROR, msg)
                success = False
        return success

    def report(self):
        '''report if the SIP status is anabled or disabled.

        :param self: essential if you override this definition
        :returns: boolean
        @note: None

        '''
        compliant = True
        sipstatus = self.getSIPStatus()
        if sipstatus == "Disabled":
            compliant = False
            msg = self.messageAppend(
                "- System Ingegrity Protection (SIP) is disabled but should be enabled!"
            )
            self.logdispatch.log(LogPriority.DEBUG, msg)
        elif sipstatus == "Enabled":
            msg = self.messageAppend(
                "- System Ingegrity Protection (SIP) is enabled!")
            self.logdispatch.log(LogPriority.DEBUG, msg)
        else:
            msg = self.messageAppend("- " + str(sipstatus))
            self.logdispatch.log(LogPriority.DEBUG, msg)
        return compliant

    def fix(self):
        fixed = True
        return fixed

    def messageGet(self):
        '''get the formatted message string.
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :returns: string
        @note: None

        '''
        return self.msg

    def messageAppend(self, pMessage=""):
        '''append and format message to the message string.
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :param pMessage:  (Default value = "")
        :returns: boolean - true
        @note: None

        '''
        datatype = type(pMessage)
        if datatype == str:
            if not (pMessage == ""):
                msg = pMessage
                if (self.msg == ""):
                    self.msg = msg
                else:
                    self.msg = self.msg + "\n" + \
                    msg
        elif datatype == list:
            if not (pMessage == []):
                for item in pMessage:
                    msg = item
                    if (self.msg == ""):
                        self.msg = msg
                    else:
                        self.msg = self.msg + "\n" + \
                        msg
        else:
            raise TypeError("pMessage with value" + str(pMessage) + \
                            "is of type " + str(datatype) + " not of " + \
                            "type " + str(str) + \
                            " or type " + str(list) + \
                            " as expected!")
        return self.msg

    def messageReset(self):
        '''reset the message string.
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :returns: boolean - true
        @note: none

        '''
        self.msg = ""
        return self.msg
Exemple #3
0
class networksetup():
    '''This objects encapsulates the complexities of the networksetup command
    on macOS (OS X)
    
    @author: ekkehard j. koch


    '''

###############################################################################

    def __init__(self, logdispatcher):
        self.location = ""
        self.locationIsValidWiFiLocation = False
        self.locationInitialized = False
        self.ns = {}
        self.nsInitialized = False
        self.nso = {}
        self.nsInitialized = False
        self.resultReset()
        self.nsc = "/usr/sbin/networksetup"
        self.logdispatch = logdispatcher

        # This class can, in no way, continue if
        # These constants are undefined, or set to
        # None
        if not DNS:
            self.logdispatch.log(LogPriority.DEBUG, "Please ensure that the following constants, in localize.py, are correctly defined and are not None: DNS, PROXY, PROXYCONFIGURATIONFILE, PROXYDOMAIN. Networksetup.py will not function without these!")
            return None
        elif DNS == None:
            self.logdispatch.log(LogPriority.DEBUG, "Please ensure that the following constants, in localize.py, are correctly defined and are not None: DNS, PROXY, PROXYCONFIGURATIONFILE, PROXYDOMAIN. Networksetup.py will not function without these!")
            return None
        if not PROXY:
            self.logdispatch.log(LogPriority.DEBUG, "Please ensure that the following constants, in localize.py, are correctly defined and are not None: DNS, PROXY, PROXYCONFIGURATIONFILE, PROXYDOMAIN. Networksetup.py will not function without these!")
            return None
        elif PROXY == None:
            self.logdispatch.log(LogPriority.DEBUG, "Please ensure that the following constants, in localize.py, are correctly defined and are not None: DNS, PROXY, PROXYCONFIGURATIONFILE, PROXYDOMAIN. Networksetup.py will not function without these!")
            return None
        if not PROXYCONFIGURATIONFILE:
            self.logdispatch.log(LogPriority.DEBUG, "Please ensure that the following constants, in localize.py, are correctly defined and are not None: DNS, PROXY, PROXYCONFIGURATIONFILE, PROXYDOMAIN. Networksetup.py will not function without these!")
            return None
        elif PROXYCONFIGURATIONFILE == None:
            self.logdispatch.log(LogPriority.DEBUG, "Please ensure that the following constants, in localize.py, are correctly defined and are not None: DNS, PROXY, PROXYCONFIGURATIONFILE, PROXYDOMAIN. Networksetup.py will not function without these!")
            return None
        if not PROXYDOMAIN:
            self.logdispatch.log(LogPriority.DEBUG, "Please ensure that the following constants, in localize.py, are correctly defined and are not None: DNS, PROXY, PROXYCONFIGURATIONFILE, PROXYDOMAIN. Networksetup.py will not function without these!")
            return None
        elif PROXYDOMAIN == None:
            self.logdispatch.log(LogPriority.DEBUG, "Please ensure that the following constants, in localize.py, are correctly defined and are not None: DNS, PROXY, PROXYCONFIGURATIONFILE, PROXYDOMAIN. Networksetup.py will not function without these!")
            return None

        fullproxy = PROXY
        self.ps = fullproxy.split(":")[-2].strip('//')
        self.pp = fullproxy.split(":")[-1]
        self.pf = PROXYCONFIGURATIONFILE
        self.dns = DNS
        self.searchdomain = PROXYDOMAIN
        self.domainByPass = PROXYDOMAINBYPASS
        self.ch = CommandHelper(self.logdispatch)
        self.initialized = False
        self.nameofdevice = ""
        self.notinservicelist = False
        self.detailedresults = ""

###############################################################################

    def report(self):
        '''report is designed to implement the report portion of the stonix rule

        :param self: essential if you override this definition
        @author: ekkehard j. koch
        @change: Breen Malmberg - 12/21/2016 - doc string revision; minor refactor;
                try/except
        :returns: compliant
        :rtype: bool

        '''

        self.logdispatch.log(LogPriority.DEBUG, "Entering networksetup.report()...\n")

        compliant = True
        if not self.initialize():
            self.logdispatch.log(LogPriority.DEBUG, "self.initialize() failed!")
        self.resultReset()

        try:

            if self.locationIsValidWiFiLocation:
                self.resultAppend("WiFi Network Setup for " + \
                                  "services for location named " + \
                                  str(self.location))
            else:
                self.resultAppend("Non-WiFi Network Setup for " + \
                                  "services for location named " + \
                                  str(self.location))

            for key in sorted(self.nso):
                network = self.nso[key]
                networkvalues = self.ns[network]

                networkname = networkvalues["name"]
                networktype = networkvalues["type"]
                networkenabled = networkvalues["enabled"]

                self.logdispatch.log(LogPriority.DEBUG, "key is " + str(key) + "\n")
                self.logdispatch.log(LogPriority.DEBUG, "network name is " + str(networkname) + "\n")
                self.logdispatch.log(LogPriority.DEBUG, "networktype is " + str(networktype) + "\n")
                self.logdispatch.log(LogPriority.DEBUG, "networkenabled is " + str(networkenabled) + "\n")
                self.logdispatch.log(LogPriority.DEBUG, "self.locationIsValidWiFiLocation is " + str(self.locationIsValidWiFiLocation) + "\n")

                if networktype == "bluetooth" and networkenabled:
                    self.logdispatch.log(LogPriority.DEBUG, "networktype is bluetooth and it is enabled. Setting compliant to False!")
                    compliant = False
                    networkvalues["compliant"] = False
                elif networktype == "wi-fi" and networkenabled and not self.locationIsValidWiFiLocation:
                    self.logdispatch.log(LogPriority.DEBUG, "networktype is wi-fi and it is enabled. This is not a valid wi-fi location. Setting compliant to False!")
                    compliant = False
                    networkvalues["compliant"] = False
                else:
                    networkvalues["compliant"] = True

                if networkvalues["compliant"]:
                    messagestring = str(networkname) + " is compliant " + \
                    ": " + str(networkvalues)
                else:
                    messagestring = str(networkname) + " is NOT " + \
                    "compliant : " + str(networkvalues)
                self.resultAppend(str(key) + " - " + messagestring)

            self.logdispatch.log(LogPriority.DEBUG, "Exiting networksetup.report() with compliant = " + str(compliant) + "\n")

        except Exception:
            raise
        return compliant

###############################################################################

    def fix(self):
        '''fix is designed to implement the fix portion of the stonix rule


        :returns: fixed

        :rtype: bool
@author: ekkehard j. koch
@change: Breen Malmberg - 1/12/2017 - added debug logging; doc string edit;
        added try/except

        '''

        self.logdispatch.log(LogPriority.DEBUG, "Entering networksetup.fix()...")

        fixed = True

        self.logdispatch.log(LogPriority.DEBUG, "Running self.initialize()...")
        if not self.initialize():
            self.logdispatch.log(LogPriority.DEBUG, "self.initialize() failed!")
        self.resultReset()

        messagestring = "for location = " + str(self.location)

        try:

            for key in sorted(self.nso):
                network = self.nso[key]
                networkvalues = self.ns[network]
                networkname = networkvalues["name"]
                networktype = networkvalues["type"]
                networkenabled = networkvalues["enabled"]

                self.logdispatch.log(LogPriority.DEBUG, "ns(key, network, networktype, networkenabled) = (" + str(key) + ", " + str(network) + ", " + str(networktype) + ", " + str(networkenabled) + ")")
                self.logdispatch.log(LogPriority.DEBUG, "self.locationIsValidWiFiLocation is " + str(self.locationIsValidWiFiLocation) + "\n")

                if networktype == "bluetooth" and networkenabled:
                    self.logdispatch.log(LogPriority.DEBUG, "Running disableNetworkService(" + str(networkname) + ")...")
                    fixedWorked = self.disableNetworkService(networkname)
                    if fixedWorked:
                        networkvalues["compliant"] = True
                        messagestring = str(networkname) + " fixed " + \
                        ": " + str(networkvalues)
                    else:
                        fixed = False

                elif networktype == "wi-fi" and networkenabled and not self.locationIsValidWiFiLocation:
                    self.logdispatch.log(LogPriority.DEBUG, "Running disableNetworkService(" + str(networkname) + ")...")
                    fixedWorked = self.disableNetworkService(networkname)
                    if fixedWorked:
                        self.logdispatch.log(LogPriority.DEBUG, "Fix worked!")
                        networkvalues["compliant"] = True
                        messagestring = str(networkname) + " fixed " + \
                        ": " + str(networkvalues)
                    else:
                        self.logdispatch.log(LogPriority.DEBUG, "Fix did NOT work!")
                        fixed = False

                elif networktype == "wi-fi" and not networkenabled and self.locationIsValidWiFiLocation:
                    self.logdispatch.log(LogPriority.DEBUG, "Running enableNetwork(" + str(networkname) + ")...")
                    fixedWorked = self.enableNetwork(networkname)
                    if fixedWorked:
                        networkvalues["compliant"] = True
                        messagestring = str(networkname) + " fixed " + \
                        ": " + str(networkvalues)
                    else:
                        fixed = False

                else:
                    networkvalues["compliant"] = True
                    messagestring = ""
                if not messagestring == "":
                    self.resultAppend(messagestring)

        except Exception:
            raise
        return fixed

###############################################################################

    def disableNetworkService(self, pNetworkName):
        '''disable network service
        
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :param pNetworkName: name of network
        :returns: boolean - true
        @note: None
        @change: Breen Malmberg - 3/23/2016 - wifi will now be disabled via
                setairportpower if not found in the service list.
        @change: Breen Malmberg - 12/20/2016 - minor refactor; parameter validation
                ;logging
        @change: Breen Malmberg - 1/12/2017 - added more debug logging

        '''

        self.logdispatch.log(LogPriority.DEBUG, "Entering networksetup.disableNetworkService()...")

        success = True
        networkName = pNetworkName.strip()

        try:

            if not isinstance(pNetworkName, str):
                self.logdispatch.log(LogPriority.DEBUG, "Specified parameter: pNetworkName must be of type: string. Got: " + str(type(pNetworkName)))
                success = False

            if not pNetworkName:
                self.logdispatch.log(LogPriority.DEBUG, "Specified parameter: pNetworkName is blank or None!")
                success = False

            self.logdispatch.log(LogPriority.DEBUG, "\nnetworkName = " + str(networkName).strip().lower() + "\n")
            self.logdispatch.log(LogPriority.DEBUG, "\nself.nameofdevice = " + str(self.nameofdevice).strip().lower() + "\n")

            if str(networkName).strip().lower() == str(self.nameofdevice).strip().lower():

                self.logdispatch.log(LogPriority.DEBUG, "networkName matches self.nameofdevice. Running airportpower disable command...")

                disablecommand = [self.nsc, "-setairportpower", networkName, "off"]

                self.ch.executeCommand(disablecommand)
                
                if self.ch.getReturnCode() != 0:
                    success = False
                    self.logdispatch.log(LogPriority.DEBUG, "Execution of command failed: " + str(disablecommand))
                else:
                    self.logdispatch.log(LogPriority.DEBUG, "Command executed successfully: " + str(disablecommand))
            else:
                if success:
                    command = [self.nsc, "-setnetworkserviceenabled", networkName, "off"]
                    self.ch.executeCommand(command)
                    if self.ch.getReturnCode() != 0:
                        success = False
                        self.logdispatch.log(LogPriority.DEBUG, "Execution of command failed: " + str(command))

            if not success:
                self.logdispatch.log(LogPriority.DEBUG, "networksetup.disableNetworkService() Failed")
            else:
                self.logdispatch.log(LogPriority.DEBUG, "networksetup.disableNetworkService() was Successful")

            self.logdispatch.log(LogPriority.DEBUG, "Exiting networksetup.disableNetworkService()")

        except Exception:
            success = False
            raise
        return success

###############################################################################

    def enableNetwork(self, pNetworkName):
        '''enable network service
        
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :param pNetworkName: name of network
        :returns: boolean - true
        @note: None
        @change: Breen Malmberg - 3/23/2016 - wifi will now be enabled via
            setairportpower if not found in the service list.
        @change: Breen Malmberg - 12/20/2016 - minor refactor; parameter validation
                ;logging

        '''

        self.logdispatch.log(LogPriority.DEBUG, "Entering networksetup.enableNetwork()...")

        success = True
        networkName = pNetworkName.strip()

        try:

            if not isinstance(pNetworkName, str):
                self.logdispatch.log(LogPriority.DEBUG, "Specified parameter: pNetworkName must be of type: string. Got: " + str(type(pNetworkName)))
                success = False

            if not pNetworkName:
                self.logdispatch.log(LogPriority.DEBUG, "Specified parameter: pNetworkName is blank or None!")
                success = False

            if str(networkName).strip().lower() == str(self.nameofdevice).strip().lower() and self.notinservicelist:
                enablecommand = [self.nsc, "-setairportpower", networkName, "on"]
                self.ch.executeCommand(enablecommand)
                if self.ch.getReturnCode() != 0:
                    success = False
                    self.logdispatch.log(LogPriority.DEBUG, "Execution of command failed: " + str(enablecommand))
            else:
                if networkName == "":
                    success = False
                if success:
                    command = [self.nsc, "-setnetworkserviceenabled", networkName, "on"]
                    self.ch.executeCommand(command)
                    if self.ch.getReturnCode() != 0:
                        success = False
                        self.logdispatch.log(LogPriority.DEBUG, "Execution of command failed: " + str(command))

            if not success:
                self.logdispatch.log(LogPriority.DEBUG, "networksetup.enableNetwork() Failed")
            else:
                self.logdispatch.log(LogPriority.DEBUG, "networksetup.enableNetwork() was Successful")

            self.logdispatch.log(LogPriority.DEBUG, "Exiting networksetup.enableNetwork()")

        except (KeyboardInterrupt, SystemExit):
# User initiated exit
            raise
        except Exception:
            raise
        return success

###############################################################################

    def getDetailedresults(self):
        '''get the detailed results text
        
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :param pLocationName: location name
        :returns: string: detailedresults
        @note: None

        '''
        return self.detailedresults

###############################################################################

    def getLocation(self):
        '''get the location used by on the mac
        
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :returns: boolean - true
        @note: None

        '''
        try:
            success = True
            command = [self.nsc, "-getcurrentlocation"]
            self.ch.executeCommand(command)
            for line in self.ch.getOutput():
                lineprocessed = line.strip()
                self.location = lineprocessed
                self.locationInitialized = True
            self.locationIsValidWiFiLocation = self.isValidLocationName(self.location)

            self.logdispatch.log(LogPriority.DEBUG, "Is this a valid WiFi location? " + str(self.locationIsValidWiFiLocation))

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            success = False
            raise
        return success

###############################################################################

    def initialize(self):
        '''initialize the object
        
        @author: ekkehard j. koch


        :returns: self.initalized

        :rtype: bool
@change: Breen Malmberg - 1/12/2017 doc string fix; default init self.initialized to False;
        added try/except

        '''

        self.initialized = False

        try:

            if not self.initialized:
                self.getLocation()
                self.updateCurrentNetworkConfigurationDictionary()
                self.initialized = True

        except Exception:
            raise
        return self.initialized

###############################################################################

    def isValidLocationName(self, pLocationName=""):
        '''determine if this is a valid wifi location
        
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :param pLocationName: location name (Default value = "")
        :returns: boolean - true
        @note: None

        '''
        success = False
        pLocationName = pLocationName.strip()
        if pLocationName == "" or re.match("^\s+$", pLocationName):
            locationName = self.location.lower().strip()
        else:
            locationName = pLocationName.lower()
        if 'wi-fi' in locationName:
            success = True
        elif 'wifi' in locationName:
            success = True
        elif 'wireless' in locationName:
            success = True
        elif 'airport' in locationName:
            success = True
        elif 'off-site' in locationName:
            success = True
        elif 'offsite' in locationName:
            success = True
        else:
            success = False
        return success

###############################################################################

    def networksetupistnetworkserviceorderoutputprocessing(self, outputLines):

        success = True
        order = -1
        networkenabled = False
        newserviceonnexline = False
        newservice = False
        servicename = ""
        networktype = False
        for line in outputLines:
            lineprocessed = line.strip()
            if newserviceonnexline:
                newservice = True
                newserviceonnexline = False
            else:
                newservice = False
                newserviceonnexline = False
            if lineprocessed == "An asterisk (*) denotes that a network service is disabled.":
                infoOnThisLine = False
                newserviceonnexline = True
            elif lineprocessed == "":
                infoOnThisLine = False
                newserviceonnexline = True
            else:
                infoOnThisLine = True
            if newservice and infoOnThisLine:
                self.logdispatch.log(LogPriority.DEBUG, "New service and info line: " + str(line))
                order = order + 1
# see if network is enabled
                if lineprocessed[:3] == "(*)":
                    networkenabled = False
                else:
                    networkenabled = True
                linearray = lineprocessed.split()
                linearray = linearray[1:]
                servicename = ""
                for item in linearray:
                    if servicename == "":
                        servicename = item
                    else:
                        servicename = servicename + " " + item

                if "ethernet" in servicename.lower():
                    networktype = "ethernet"
                elif "lan" in servicename.lower():
                    #####
                    # The belkin dongles LANL has chosen to use for Apple
                    # laptops does not identify itself vi convention,
                    # so this is the choice roy is making to indicate the
                    # mapping between "Belkin USB-C LAN" and ethernet.
                    networktype = "ethernet"
                elif "bluetooth" in servicename.lower():
                    networktype = "bluetooth"
                elif "usb" in servicename.lower():
                    networktype = "usb"
                elif "wi-fi" in item.lower():
                    networktype = "wi-fi"
                elif "firewire" in servicename.lower():
                    networktype = "firewire"
                elif "thunderbolt" in servicename.lower():
                    networktype = "thunderbolt"
                else:
                    networktype = "unknown"
                self.ns[servicename] = {"name": servicename.strip(),
                                        "hardware port":  servicename.strip(),
                                        "enabled": networkenabled,
                                        "type": networktype.strip()}
# determine network type
            elif infoOnThisLine:
                self.logdispatch.log(LogPriority.DEBUG, "Info line: " + str(line))
                lineprocessed = lineprocessed.strip("(")
                lineprocessed = lineprocessed.strip(")")
                linearray = lineprocessed.split(",")
                for item in linearray:
                    lineprocessed = item.strip()
                    itemarray = lineprocessed.split(":")
                    if servicename != "":
                        if len(itemarray) > 1:
                            self.ns[servicename][itemarray[0].strip().lower()] = itemarray[1].strip()
# update dictionary entry for network
                    self.logdispatch.log(LogPriority.DEBUG, "(servicename, enabled, networktype): (" + \
                                         str(servicename).strip() + ", " + str(networkenabled) + ", " + \
                                         str(networktype).strip() + ")")
# create an ordered list to look up later
                    orderkey = str(order).zfill(4)
                    self.nso[orderkey] = servicename.strip()
                    self.updateNetworkConfigurationDictionaryEntry(servicename.strip())
        self.setNetworkServiceOrder()
        return success

###############################################################################

    def networksetuplistallhardwareportsoutputprocessing(self, outputLines):

        success = True
        newserviceonnexline = False
        newservice = False
        servicename = ""
        # noinfo = False
        for line in outputLines:
            lineprocessed = line.strip()
            if newserviceonnexline:
                newservice = True
                newserviceonnexline = False
            else:
                newservice = False
                newserviceonnexline = False
            if lineprocessed == "":
                infoOnThisLine = False
                newserviceonnexline = True
            else:
                infoOnThisLine = True
# Get info from first new service line
            if newserviceonnexline and not servicename == "":
                self.updateNetworkConfigurationDictionaryEntry(servicename)
            elif lineprocessed == "VLAN Configurations":
                break
            elif newservice and infoOnThisLine:
                self.logdispatch.log(LogPriority.DEBUG, "New service and info line: " + str(line))
                linearray = lineprocessed.split(":")
                linearray = linearray[1:]
                servicename = ""
                for item in linearray:
                    if servicename == "":
                        servicename = item.strip()
                    else:
                        servicename = servicename + " " + item.strip()
                if "ethernet" in servicename.lower():
                    networktype = "ethernet"
                elif "lan" in servicename.lower():
                    #####
                    # The belkin dongles LANL has chosen to use for Apple
                    # laptops does not identify itself vi convention,
                    # so this is the choice roy is making to indicate the
                    # mapping between "Belkin USB-C LAN" and ethernet.
                    networktype = "ethernet"
                elif "bluetooth" in servicename.lower():
                    networktype = "bluetooth"
                elif "usb" in servicename.lower():
                    networktype = "usb"
                elif "wi-fi" in servicename.lower():
                    networktype = "wi-fi"
                elif "firewire" in servicename.lower():
                    networktype = "firewire"
                elif "thunderbolt" in servicename.lower():
                    networktype = "thunderbolt"
                else:
                    networktype = "unknown"
                self.ns[servicename] = {"name": servicename.strip(),
                                        "hardware port": servicename.strip(),
                                        "type": networktype.strip()}
# determine network type
            elif infoOnThisLine:
                self.logdispatch.log(LogPriority.DEBUG, "Info line: " + str(line))
                linearray = lineprocessed.split()
                colonFound = False
                nameOfItem = ""
                valueOfItem = ""
                for item in linearray:
                    processedItem = item.strip()
                    if not colonFound:
                        if ":" in item:
                            colonFound = True
                            processedItem = item.strip(":")
                        if nameOfItem == "":
                            nameOfItem = processedItem.lower()
                        else:
                            nameOfItem = nameOfItem + " " + processedItem.lower()
                    else:
                        if valueOfItem == "":
                            valueOfItem = processedItem
                        else:
                            valueOfItem = valueOfItem + " " + processedItem
                if not valueOfItem == "" and not nameOfItem == "":
                    self.ns[servicename][nameOfItem] = valueOfItem.strip()
        return success

###############################################################################

    def resultAppend(self, pMessage=""):
        '''reset the current kveditor values to their defaults.
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :param pMessage: message to be appended (Default value = "")
        :returns: boolean - true
        @note: None

        '''
        datatype = type(pMessage)
        if datatype == str:
            if not (pMessage == ""):
                messagestring = pMessage
                if (self.detailedresults == ""):
                    self.detailedresults = messagestring
                else:
                    self.detailedresults = self.detailedresults + "\n" + \
                                           messagestring
        elif datatype == list:
            if not (pMessage == []):
                for item in pMessage:
                    messagestring = item
                    if (self.detailedresults == ""):
                        self.detailedresults = messagestring
                    else:
                        self.detailedresults = self.detailedresults + "\n" + \
                        messagestring
        else:
            raise TypeError("pMessage with value" + str(pMessage) + \
                            "is of type " + str(datatype) + " not of " + \
                            "type " + str(str) + \
                            " or type " + str(list) + \
                            " as expected!")

###############################################################################

    def resultReset(self):
        '''reset the current kveditor values to their defaults.
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :returns: boolean - true
        @note: kveditorName is essential

        '''
        self.detailedresults = ""

###############################################################################

    def setAdvancedNetworkSetup(self, pHardwarePort = None):
        '''Set proxies up for normal first configuration that has a network
        connection.
        
        @author: Roy Nielsen

        :param self: essential if you override this definition
        :param pNetworkName: name of the network to fix
        :param pHardwarePort:  (Default value = None)
        :returns: boolean - true
        @note: None

        '''
        success = True
        if pHardwarePort == None:
            self.initialize()
            self.setNetworkServiceOrder()
            for key in sorted(self.nso):
                network = self.nso[key]
                networkvalues = self.ns[network]
                networkname = networkvalues["name"]
                networktype = networkvalues["type"]
                networkhardwarePort = networkvalues["hardware port"]
                networkenabled = networkvalues["enabled"]
                msg = "networkname " + str(networkname) + "; networktype " + str(networktype) + \
                "; networkhardwarePort " + str(networkhardwarePort) + "; networkenabled " + \
                str(networkenabled)
                self.logdispatch.log(LogPriority.DEBUG, msg)
                if networkenabled and (networktype == "wifi" or networktype == "ethernet"):
                    msg = "Enabled Network Found; " + msg
                    self.logdispatch.log(LogPriority.DEBUG, msg)
                    break
        else:
            networkhardwarePort = pHardwarePort.strip()
            networkenabled = True
# Set the DNS servers
        if not networkhardwarePort == "" and networkenabled:
            command = self.nsc + " -setdnsservers '" + str(networkhardwarePort) + "' " + self.dns
            self.ch.executeCommand(command)
            if self.ch.getError():
                msg = command + " output: " + str(self.ch.getOutput())
                self.logdispatch.log(LogPriority.DEBUG, msg)
                success = False
# Set the Search Domain
            command = self.nsc + " -setsearchdomains '" + str(networkhardwarePort) + "' " + self.searchdomain
            self.ch.executeCommand(command)
            if self.ch.getError():
                msg = command + " output: " + str(self.ch.getOutput())
                self.logdispatch.log(LogPriority.DEBUG, msg)
                success = False
# set up the auto proxy URL
            command = self.nsc + " -setautoproxyurl '" + str(networkhardwarePort) + "' " + self.pf
            self.ch.executeCommand(command)
            if self.ch.getError():
                msg = command + " output: " + str(self.ch.getOutput())
                self.logdispatch.log(LogPriority.DEBUG, msg)
                success = False
# Set up the FTP proxy
            command = self.nsc + " -setftpproxy '" + str(networkhardwarePort) + "' " + self.ps + " " + self.pp
            self.ch.executeCommand(command)
            if self.ch.getError():
                msg = command + " output: " + str(self.ch.getOutput())
                self.logdispatch.log(LogPriority.DEBUG, msg)
                success = False
# Set up the HTTPS proxy
            command = self.nsc + " -setsecurewebproxy '" + str(networkhardwarePort) + "' " + self.ps + " " + self.pp
            self.ch.executeCommand(command)
            if self.ch.getError():
                msg = command + " output: " + str(self.ch.getOutput())
                self.logdispatch.log(LogPriority.DEBUG, msg)
                success = False
# Set up the web proxy
            command = self.nsc + " -setwebproxy '" + str(networkhardwarePort) + "' " + self.ps + " " + self.pp
            self.ch.executeCommand(command)
            if self.ch.getError():
                msg = command + " output: " + str(self.ch.getOutput())
                self.logdispatch.log(LogPriority.DEBUG, msg)
                success = False
# Get current proxy bypass domains and add self.searchdomain
            command = self.nsc + " -getproxybypassdomains '" + str(networkhardwarePort) + "' "
            self.ch.executeCommand(command)
            if not self.ch.getError():
                command = self.nsc + " -setproxybypassdomains '" + str(networkhardwarePort) + "'"
                for item in self.ch.getOutput() :
                    if not re.match("^\s*$", item) :
                        command = command + " " + str(item.strip())
                if not self.domainByPass in command:
                    command = command + " " + str(self.domainByPass)
                    self.ch.executeCommand(command)
                    if not self.ch.getError():
                        success = False
            else:
                msg = command + " output: " + str(self.ch.getOutput())
                self.logdispatch.log(LogPriority.DEBUG, msg)
                success = False
        return success

###############################################################################

    def setNetworkServiceOrder(self):
        ''' '''
        #####
        # Find the interface that needs to be at the top of the self.nso order
        cmd = ["/sbin/route", "get", "default"]

        self.ch.executeCommand(cmd)
        defaultInterface = None

        for line in self.ch.getOutput():
            try:
                interface_match = re.match("\s+interface:\s+(\w+)", line)
                defaultInterface = interface_match.group(1)
            except (IndexError, KeyError, AttributeError) as err:
                self.logdispatch.log(LogPriority.DEBUG, str(line) + " : " + str(err))
            else:
                self.logdispatch.log(LogPriority.DEBUG, "Found: " + str(line))
                break

        #####
        # Find the interface/service name via networksetup -listallhardwareports
        cmd = ["/usr/sbin/networksetup", "-listallhardwareports"]

        self.ch.executeCommand(cmd)

        hardwarePort = ""
        device = ""
        enet = ""

        for line in self.ch.getOutput():
            try:
                hw_match = re.match("^Hardware Port:\s+(.*)\s*$", line)
                hardwarePort = hw_match.group(1)
                #print hardwarePort
            except AttributeError as err:
                pass
            try:
                #print line
                dev_match = re.match("^Device:\s+(.*)\s*$", line)
                device = dev_match.group(1)
                #print str(device)
            except AttributeError as err:
                pass
            try:
                enet_match = re.match("^Ethernet Address:\s+(\w+:\w+:\w+:\w+:\w+:\w+)\s*$", line)
                enet = enet_match.group(1)
                self.logger.log(LogPriority.DEBUG, "enet: " + str(enet))
            except AttributeError as err:
                pass

            if re.match("^$", line) or re.match("^\s+$", line):
                if re.match("^%s$"%str(device), str(defaultInterface)):
                    self.logdispatch.log(LogPriority.DEBUG, device)
                    self.logdispatch.log(LogPriority.DEBUG,  defaultInterface)
                    break
                hardwarePort = ""
                device = ""
                enet = ""

        #####
        # Reset NSO order if the defaultInterface is not at the top of the list
        newnso = {}
        i = 1

        self.logdispatch.log(LogPriority.DEBUG, str(self.nso))
        self.logdispatch.log(LogPriority.DEBUG, "hardware port: " + hardwarePort)

        for key, value in sorted(self.nso.items()):
            #print str(key) + " : " + str(value)
            if re.match("^%s$"%hardwarePort.strip(), value.strip()):
                key = re.sub("^\d\d\d\d$", "0000", key)
                newnso[key] = value
            else:
                orderkey = str(i).zfill(4)
                newnso[orderkey] = value
                i = i + 1
                self.logdispatch.log(LogPriority.DEBUG, str(newnso))
        #print str(newnso)
        self.nso = newnso
        self.logdispatch.log(LogPriority.DEBUG, str(self.nso))
        for key, value in sorted(self.nso.items()):
            self.logdispatch.log(LogPriority.DEBUG, str(key) + " : " + str(value))

        for item in self.ns: 
            if re.match("^%s$"%hardwarePort.strip(), self.ns[item]["name"]) and self.ns[item]["type"] is "unknown" and re.match("^en", defaultInterface):
                self.ns[item]["type"] = "ethernet"

###############################################################################

    def startup(self):
        '''startup is designed to implement the startup portion of the stonix rule
        
        @author: ekkehard j. koch


        '''
        disabled = True
        self.initialize()
        messagestring = "for location = " + str(self.location)
        for key in sorted(self.nso):
            network = self.nso[key]
            networkvalues = self.ns[network]
            networkname = networkvalues["name"]
            networktype = networkvalues["type"]
            networkenabled = networkvalues["enabled"]
            if networktype == "bluetooth" and networkenabled:
                fixedWorked = self.disableNetworkService(networkname)
                if fixedWorked:
                    networkvalues["compliant"] = True
                    messagestring = str(networkname) + " fixed " + \
                    ": " + str(networkvalues)
                else:
                    disabled = False
            elif networktype == "wi-fi" and networkenabled:
                fixedWorked = self.disableNetworkService(networkname)
                if fixedWorked:
                    networkvalues["compliant"] = True
                    messagestring = str(networkname) + " fixed " + \
                    ": " + str(networkvalues)
                else:
                    disabled = False
            else:
                networkvalues["compliant"] = True
                messagestring = ""
            if not messagestring == "":
                self.resultAppend(messagestring)
        return disabled

###############################################################################

    def updateCurrentNetworkConfigurationDictionary(self):
        '''update the network configuration dictianry
        
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :returns: boolean - true
        @note: None
        @change: Breen Malmberg - 3/23/2016 - added code to find and disable
                wi-fi on el capitan, via hardware ports instead of just service

        '''

        self.logdispatch.log(LogPriority.DEBUG, "Entering updateCurrentNetworkConfigurationDictionary()...")

        try:

            success = True

# issue networksetup -listallhardwareports to get all network services
            if success:
                command = [self.nsc, "-listallhardwareports"]
                self.ch.executeCommand(command)
                self.logdispatch.log(LogPriority.DEBUG, "Building ns dictionary from command: " + str(command))
                success = self.networksetuplistallhardwareportsoutputprocessing(self.ch.getOutput())

# issue networksetup -listallnetworkservices to get all network services
            if success:
                command = [self.nsc, "-listnetworkserviceorder"]
                self.ch.executeCommand(command)
                self.logdispatch.log(LogPriority.DEBUG, "Building ns dictionary from command: " + str(command))
                success = self.networksetupistnetworkserviceorderoutputprocessing(self.ch.getOutput())

# set ns init and nso init status
            self.nsInitialized = True
            self.nsoInitialized = True

            self.logdispatch.log(LogPriority.DEBUG, "Exiting updateCurrentNetworkConfigurationDictionary()...")

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            success = False
            raise
        return success

###############################################################################

    def updateNetworkConfigurationDictionaryEntry(self, pKey):
        '''update a single network configuration dictionary entry
        
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :param pkey: key for the dictinary entry
        :param pKey: 
        :returns: boolean - true
        @note: None
        @change: Breen Malmberg - 1/12/2017 - doc string edit; added debug logging;
                default var init success to True; added code to update the Wi-Fi entry;
        @change: Roy Nielsen - 3/6/2018 - Changed algo to look at
                                          'Device' rather than 'name'
                                          when getting the airport power
                                          status

        '''
        pKey = pKey.strip()
        self.logdispatch.log(LogPriority.DEBUG, "Entering networksetup.updateNetworkConfigurationDictionaryEntry() with pKey=" + str(pKey) + "...")

        success = True
        key = pKey

        try:
            success = True
            key = pKey
            entry = self.ns[key]

            if success:
                if entry == None:
                    success = False
                    self.logdispatch.log(LogPriority.DEBUG, "self.ns[" + str(key) + "] was not found! success set to False.")

            if success:
                command = [self.nsc, "-getmacaddress", key]
                self.ch.executeCommand(command)
                for line in self.ch.getOutput():
                    try:
                        macaddress = re.search("(\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)",
                                               line.strip()).group(1)
                    except:
                        macaddress = ""
                    self.ns[key]["macaddress"] = macaddress

            if success:
                # added for disabling by device name 1/11/2017
                if key == "Wi-Fi":
                    self.logdispatch.log(LogPriority.DEBUG, "Updating Wi-Fi device entry for: " + str(self.ns[key]["name"]))
                    command = [self.nsc, "-getairportpower", self.ns[key]["Device"]]
                    self.ch.executeCommand(command)
                    for line in self.ch.getOutput():
                        if re.search("Wi-Fi\s+Power.*On", line, re.IGNORECASE):
                            self.ns[key]["enabled"] = True
                            self.logdispatch.log(LogPriority.DEBUG, "airportpower for device " + str(self.ns[key]["name"]) + " is: On")
                        else:
                            self.ns[key]["enabled"] = False
                            self.logdispatch.log(LogPriority.DEBUG, "airportpower for device " + str(self.ns[key]["name"]) + " is: Off")
                else:
                    # original code (only for services)
                    command = [self.nsc,
                           "-getnetworkserviceenabled", key]
                    self.ch.executeCommand(command)
                    for line in self.ch.getOutput():
                        lineprocessed = line.strip()
                        if lineprocessed == "Enabled":
                            self.ns[key]["enabled"] = True
                        else:
                            self.ns[key]["enabled"] = False

            self.logdispatch.log(LogPriority.DEBUG, "Exiting networksetup.updateNetworkConfigurationDictionaryEntry() and returning success=" + str(success))
        except KeyError:
            self.logdispatch.log(LogPriority.DEBUG, "Key error...")
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            success = False
            raise

        return success
Exemple #4
0
class LaunchCtl(object):
    '''Service manager that provides an interface to the Mac OS launchctl command.
    
    @privatemethod: validateSubCommand - validate a command that is
                    formatted as: { <subcommand> : [<arg1>, <arg1>, <arg3>]}
                    where each argN is a string.
    
    @privatemethod: runSubCommand - runs a launchctl command, in the format
                    described above, then collects standard out, standard
                    error and the launchctl return code, returning them to the
                    caller.
    
    # -------------------------------------------------------------------------
    Legacy commands
    
    @publicmethod: load - (legacy) loads the passed in plist/service
    
    @publicmethod: unload - (legacy) unloads the passed in plist/service
    
    @publicmethod: start - (legacy) Start a service
    
    @publicmethod: stop - (legacy) Stop a service
    
    @publicmethod: list - (legacy) list a specific plist state, or
                   returns a list of running launchd services.
    
    @publicmethod: bsexec - (legacy) execute a command in as close as possible
                            context to the passed in PID.
    
    @publicmethod: asuser - (legacy) execute a command in as close as possible
                            context to the passed in UID
    
    # -------------------------------------------------------------------------
    Current commands
    
    @publicmethod: bootstrap
    
    @publicmethod: bootout
    
    @publicmethod: enable
    
    @publicmethod: disable
    
    @publicmethod: uncache
    
    @publicmethod: kickstart
    
    @publicmethod: kill - (2.0) Kills a service, with one of the passed
                   in signals that are described in the Mac OS signal(3)
                   manpage.
    
    @publicmethod: blame
    
    @publicmethod: printTarget
    
    @publicmethod: printCache
    
    @publicmethod: printDisabled
    
    @publicmethod: procinfo
    
    @publicmethod: hostinfo
    
    @publicmethod: resolveport
    
    @publicmethod: reboot
    
    @Note: Future subcommands may include 'plist', 'config', 'error'.
    
    @author: Roy Nielsen


    '''
    def __init__(self, logger):
        """
        Initialization Method

        @author: Roy Nielsen
        """
        self.launchctl = "/bin/launchctl"
        self.logger = logger
        self.ch = CommandHelper(self.logger)

    # ----------------------------------------------------------------------
    # helper methods
    # ----------------------------------------------------------------------

    def isSaneFilePath(self, filepath):
        '''Check for a good file path in the passed in string.
        
        @author: Roy Nielsen

        :param filepath: 

        '''
        sane = False
        if isinstance(filepath, str):
            if re.match("^[A-Za-z/\.][A-Za-z0-9/\._-]*", filepath):
                sane = True
            else:
                self.logger.log(
                    lp.DEBUG, "filepath: " + str(filepath) + " is not valid.")
        return sane

    # ----------------------------------------------------------------------

    def validateSubCommand(self, command={}):
        '''Validate that we have a properly formatted command, and the subcommand
        is valid.

        :param command:  (Default value = {})
        :returns: s: success - whether the command was formatted correctly or not.
        
        @author: Roy Nielsen

        '''
        success = False
        subcmd = []
        if not isinstance(command, dict):
            self.logger.log(lp.ERROR, "Command must be a dictionary...")
        else:
            commands = 0
            for subCommand, args in list(command.items()):
                commands += 1
                #####
                # Check to make sure only one command is in the dictionary
                if commands > 1:
                    self.logger.log(
                        lp.ERROR, "Damn it Jim! One command at " + "a time!!")
                    success = False
                    break
                #####
                # Check if the subcommand is a valid subcommand...
                validSubcommands = [
                    "load", "unload", "start", "stop", "list", "bsexec",
                    "asuser", "bootstrap", "bootout", "enable", "disable",
                    "uncache", "kickstart", "kill", "blame", "print",
                    "print-cache", "print-disabled", "procinfo", "hostinfo",
                    "resolveport", "reboot"
                ]
                if subCommand not in validSubcommands:
                    success = False
                    break
                else:
                    success = True
                #####
                # Check to make sure the key or subCommand is a string, and
                # the value is alist and args are
                if not isinstance(subCommand, str) or \
                   not isinstance(args, list):
                    self.logger.log(
                        lp.ERROR, "subcommand needs to be a " +
                        "string, and args needs to be a list " + "of strings")
                    success = False
                else:
                    #####
                    # Check the arguments to make sure they are all strings
                    for arg in args:
                        if not isinstance(arg, str):
                            self.logger.log(
                                lp.ERROR, "Arg '" + str(arg) +
                                "'needs to be a string...")
                            success = False
                    if success:
                        subcmd = [subCommand] + args
                        self.logger.log(lp.DEBUG, 'subcmd: ' + str(subcmd))
        return success, subcmd

    # -------------------------------------------------------------------------

    def runSubCommand(self, commandDict={}):
        '''Use the passed in dictionary to create a MacOS 'security' command
        and execute it.

        :param commandDict:  (Default value = {})
        :returns: s: success - whether the command was successfull or not.
        
        @author: Roy Nielsen

        '''
        success = False
        output = ''
        error = ''
        returncode = ''
        #####
        # Make sure the command dictionary was properly formed, as well as
        # returning the formatted subcommand list
        validationSuccess, subCmd = self.validateSubCommand(commandDict)
        if validationSuccess:
            #####
            # Command setup - note that the keychain deliberately has quotes
            # around it - there could be spaces in the path to the keychain,
            # so the quotes are required to fully resolve the file path.
            # Note: this is done in the build of the command, rather than
            # the build of the variable.
            cmd = [self.launchctl] + subCmd
            self.logger.log(lp.DEBUG, 'cmd: ' + str(cmd))
            #####
            # set up and run the command
            # self.ch.setCommand(cmd)
            success = self.ch.executeCommand(cmd)

            output = self.ch.getOutput()
            error = self.ch.getError()
            returncode = self.ch.getReturnCode()
            #####
            # If the return code is 0, then we have success.
            if not returncode:
                success = True
                """
                if "bootstrap" in subCmd:
                    raise ValueError("cmd: " + str(cmd) + " output: " +
                                     str(output) + " error: " + str(error) +
                                     " retcode: " + str(returncode))
                """

            if error:
                self.logger.log(lp.INFO, "Output: " + str(output))
                self.logger.log(lp.INFO, "Error: " + str(error))
                self.logger.log(lp.INFO, "Return code: " + str(returncode))
                success = False

        else:
            raise ValueError

        return success, str(output), str(error), str(returncode)

    # ----------------------------------------------------------------------
    # Legacy Subcommands
    # ----------------------------------------------------------------------

    def load(self, plist="", options="", sessionType="", domain=False):
        '''@note: From the launchctl man page:
          load | unload [-wF] [-S sessiontype] [-D domain] paths ...
              Load the specified configuration files or directories of con-
              figuration files.  Jobs that are not on-demand will be started
              as soon as possible. All specified jobs will be loaded before
              any of them are allowed to start. Note that per-user configura-
              tion files (LaunchAgents) must be owned by root (if they are
              located in /Library/LaunchAgents) or the user loading them (if
              they are located in $HOME/Library/LaunchAgents).  All system-
              wide daemons (LaunchDaemons) must be owned by root. Configura-
              tion files must disallow group and world writes. These restric-
              tions are in place for security reasons, as allowing writabil-
              ity to a launchd configuration file allows one to specify which
              executable will be launched.
        
              Note that allowing non-root write access to the
              /System/Library/LaunchDaemons directory WILL render your system
              unbootable.
        
              -w       Overrides the Disabled key and sets it to false or
                       true for the load and unload subcommands respectively.
                       In previous versions, this option would modify the
                       configuration file. Now the state of the Disabled key
                       is stored elsewhere on- disk in a location that may
                       not be directly manipulated by any process other than
                       launchd.
        
              -F       Force the loading or unloading of the plist. Ignore
                       the Disabled key.
        
              -S sessiontype
                       Some jobs only make sense in certain contexts. This
                       flag instructs launchctl to look for jobs in a differ-
                       ent location when using the -D flag, and allows
                       launchctl to restrict which jobs are loaded into which
                       session types. Sessions are only relevant for per-user
                       launchd contexts. Relevant sessions are Aqua (the
                       default), Background and LoginWindow.  Background
                       agents may be loaded independently of a GUI login.
                       Aqua agents are loaded only when a user has logged in
                       at the GUI. LoginWindow agents are loaded when the
                       LoginWindow UI is displaying and currently run as
                       root.
        
              -D domain
                       Look for plist(5) files ending in *.plist in the
                       domain given. This option may be thoughts of as
                       expanding into many individual paths depending on the
                       domain name given. Valid domains include "system,"
                       "local," "network" and "all." When providing a session
                       type, an additional domain is available for use called
                       "user." For example, without a session type given, "-D
                       system" would load from or unload property list files
                       from /System/Library/LaunchDaemons.  With a session
                       type passed, it would load from /System/Library/Laun-
        
              NOTE: Due to bugs in the previous implementation and long-
              standing client expectations around those bugs, the load and
              unload subcommands will only return a non-zero exit code due to
              improper usage.  Otherwise, zero is always returned.
        
        @author: Roy Nielsen

        :param plist:  (Default value = "")
        :param options:  (Default value = "")
        :param sessionType:  (Default value = "")
        :param domain:  (Default value = False)

        '''
        success = False
        #####
        # Input validation.
        if self.isSaneFilePath(plist):
            args = []

            if re.match("[-wF]+", str(options)) and \
               isinstance(options, str):
                args.append(options)
            else:
                self.logger.log(
                    lp.INFO,
                    "Need a the options to be a single" + " string...")

            sessionTypes = ['Aqua', 'StandardIO', 'Background', 'LoginWindow']
            if sessionType in sessionTypes:
                args += ['-S', sessionType]
            else:
                self.logger.log(
                    lp.INFO, "Need a the sessionType in: " + str(sessionTypes))

            if isinstance(domain, str):
                args += ['-D', domain]
            else:
                self.logger.log(lp.INFO,
                                "Need a the domain in: " + str(sessionTypes))

            args.append(plist)

            cmd = {"load": args}
            success, _, stderr, _ = self.runSubCommand(cmd)

            if not success and re.search("already loaded", stderr):
                success = True

        return success

    # -------------------------------------------------------------------------

    def unLoad(self, plist="", options="", sessionType="", domain=False):
        '''@note: From the launchctl man page:
          load | unload [-wF] [-S sessiontype] [-D domain] paths ...
              Load the specified configuration files or directories of con-
              figuration files.  Jobs that are not on-demand will be started
              as soon as possible. All specified jobs will be loaded before
              any of them are allowed to start. Note that per-user configura-
              tion files (LaunchAgents) must be owned by root (if they are
              located in /Library/LaunchAgents) or the user loading them (if
              they are located in $HOME/Library/LaunchAgents).  All system-
              wide daemons (LaunchDaemons) must be owned by root. Configura-
              tion files must disallow group and world writes. These restric-
              tions are in place for security reasons, as allowing writabil-
              ity to a launchd configuration file allows one to specify which
              executable will be launched.
        
              Note that allowing non-root write access to the
              /System/Library/LaunchDaemons directory WILL render your system
              unbootable.
        
              -w       Overrides the Disabled key and sets it to false or
                       true for the load and unload subcommands respectively.
                       In previous versions, this option would modify the
                       configuration file. Now the state of the Disabled key
                       is stored elsewhere on- disk in a location that may
                       not be directly manipulated by any process other than
                       launchd.
        
              -F       Force the loading or unloading of the plist. Ignore
                       the Disabled key.
        
              -S sessiontype
                       Some jobs only make sense in certain contexts. This
                       flag instructs launchctl to look for jobs in a differ-
                       ent location when using the -D flag, and allows
                       launchctl to restrict which jobs are loaded into which
                       session types. Sessions are only relevant for per-user
                       launchd contexts. Relevant sessions are Aqua (the
                       default), Background and LoginWindow.  Background
                       agents may be loaded independently of a GUI login.
                       Aqua agents are loaded only when a user has logged in
                       at the GUI. LoginWindow agents are loaded when the
                       LoginWindow UI is displaying and currently run as
                       root.
        
              -D domain
                       Look for plist(5) files ending in *.plist in the
                       domain given. This option may be thoughts of as
                       expanding into many individual paths depending on the
                       domain name given. Valid domains include "system,"
                       "local," "network" and "all." When providing a session
                       type, an additional domain is available for use called
                       "user." For example, without a session type given, "-D
                       system" would load from or unload property list files
                       from /System/Library/LaunchDaemons.  With a session
                       type passed, it would load from /System/Library/Laun-
        
              NOTE: Due to bugs in the previous implementation and long-
              standing client expectations around those bugs, the load and
              unload subcommands will only return a non-zero exit code due to
              improper usage.  Otherwise, zero is always returned.
        
        @author: Roy Nielsen

        :param plist:  (Default value = "")
        :param options:  (Default value = "")
        :param sessionType:  (Default value = "")
        :param domain:  (Default value = False)

        '''
        success = False
        #####
        # Input validation.
        if self.isSaneFilePath(plist):
            args = []

            if re.match("[-wF]+", str(options)) and \
               isinstance(options, str):
                args.append(options)
            else:
                self.logger.log(
                    lp.INFO,
                    "Need a the options to be a single" + " string...")

            sessionTypes = ['Aqua', 'StandardIO', 'Background', 'LoginWindow']
            if sessionType in sessionTypes:
                args += ['-S', sessionType]
            else:
                self.logger.log(
                    lp.INFO, "Need a the sessionType in: " + str(sessionTypes))

            if isinstance(domain, str):
                args += ['-D', domain]
            else:
                self.logger.log(lp.INFO,
                                "Need a the domain in: " + str(sessionTypes))

            args.append(plist)

            cmd = {"unload": args}
            success, _, stderr, _ = self.runSubCommand(cmd)
            if not success and re.search('Could not find specified', stderr):
                success = True

        return success

    # -------------------------------------------------------------------------

    def start(self, label=""):
        '''@note: From the launchctl man page:
          start label
              Start the specified job by label. The expected use of this sub-
              command is for debugging and testing so that one can manually
              kick-start an on-demand server.
        
        @author: Roy Nielsen

        :param label:  (Default value = "")

        '''
        success = False
        #####
        # Input validation.
        if not label or not isinstance(label, str):
            return success

        cmd = {"start": label}
        success, _, _, _ = self.runSubCommand(cmd)

        return success

    # -------------------------------------------------------------------------

    def stop(self, label=""):
        '''@note: From the launchctl man page:
          stop label
              Stop the specified job by label. If a job is on-demand, launchd
              may immediately restart the job if launchd finds any criteria
              that is satisfied.
        
        @author: Roy Nielsen

        :param label:  (Default value = "")

        '''
        success = False
        #####
        # Input validation.
        if not label or not isinstance(label, str):
            return success

        cmd = {"stop": label}
        success, _, _, _ = self.runSubCommand(cmd)

        return success

    # -------------------------------------------------------------------------

    def list(self, label=""):
        '''@note: From the launchctl man page:
          list [-x] [label]
              With no arguments, list all of the jobs loaded into launchd in
              three columns. The first column displays the PID of the job if
              it is running.  The second column displays the last exit status
              of the job. If the number in this column is negative, it repre-
              sents the negative of the signal which stopped the job. Thus,
              "-15" would indicate that the job was terminated with SIGTERM.
              The third column is the job's label. If [label] is specified,
              prints information about the requested job.
        
              -x       This flag is no longer supported.
        
        @author: Roy Nielsen

        :param label:  (Default value = "")

        '''
        success = False
        #####
        # Input validation.
        if label and isinstance(label, str):
            cmd = [self.launchctl, 'list', label]
        elif not label:
            cmd = [self.launchctl, 'list']
        else:
            return success
        #####
        # set up and run the command
        # self.ch.setCommand(cmd)
        success = self.ch.executeCommand(cmd)

        output = self.ch.getOutput()
        error = self.ch.getError()
        returncode = self.ch.getReturnCode()

        if not error:
            self.logger.log(lp.DEBUG, "Output: " + str(output))
            self.logger.log(lp.DEBUG, "Error: " + str(error))
            self.logger.log(lp.DEBUG, "Return code: " + str(returncode))
            success = True
        else:
            self.logger.log(lp.DEBUG, "Output: " + str(output))
            self.logger.log(lp.DEBUG, "Error: " + str(error))
            self.logger.log(lp.DEBUG, "Return code: " + str(returncode))
            success = False

        return success, output, error, returncode

    # -------------------------------------------------------------------------

    def bsExec(self, pid, command, args=[]):
        '''@note: From the launchctl man page:
          bsexec PID command [args]
              This executes the given command in as similar an execution con-
              text as possible to the target PID. Adopted attributes include
              the Mach bootstrap namespace, exception server and security
              audit session. It does not modify the process' credentials
              (UID, GID, etc.) or adopt any environment variables from the
              target process. It affects only the Mach bootstrap context and
              directly-related attributes.
        
        @author: Roy Nielsen

        :param pid: 
        :param command: 
        :param args:  (Default value = [])

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(pid, int) or \
           not isinstance(command, str) or \
           not isinstance(args, list):
            return success

        cmd = {"bsexec": [pid, command] + args}
        success, _, _, _ = self.runSubCommand(cmd)

        return success

    # -------------------------------------------------------------------------

    def asUser(self, uid, command, args=[]):
        '''@note: From the launchctl man page:
          asuser UID command [args]
              This executes the given command in as similar an execution con-
              text as possible to that of the target user's bootstrap.
              Adopted attributes include the Mach bootstrap namespace, excep-
              tion server and security audit session. It does not modify the
              process' credentials (UID, GID, etc.) or adopt any user-spe-
              cific environment variables. It affects only the Mach bootstrap
              context and directly- related attributes.
        
        @author: Roy Nielsen

        :param uid: 
        :param command: 
        :param args:  (Default value = [])

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(uid, int) or \
           not isinstance(command, str) or \
           not isinstance(args, list):
            return success

        cmd = {"asuser": [uid, command] + args}
        success, stdout, stderr, retcode = self.runSubCommand(cmd)

        if retcode != '0':
            raise ValueError(reportStack() + "- success: " + str(success) +
                             " stdout: " + str(stdout) + " stderr: " +
                             str(stderr) + " retcode: " + str(retcode))
        return success

    # ----------------------------------------------------------------------
    # Supported Second generation subcommands
    # ----------------------------------------------------------------------

    def bootStrap(self, domainTarget="", servicePath=''):
        '''@note: From the launchctl man page:
          bootstrap | bootout domain-target [service-path service-path2 ...] |
              service-target
              Bootstraps or removes domains and services. Services may be
              specified as a series of paths or a service identifier. Paths
              may point to XPC service bundles, launchd.plist(5) s, or a
              directories containing a collection of either. If there were
              one or more errors while bootstrapping or removing a collection
              of services, the problematic paths will be printed with the
              errors that occurred.
        
              If no paths or service target are specified, these commands can
              either bootstrap or remove a domain specified as a domain tar-
              get. Some domains will implicitly bootstrap pre-defined paths
              as part of their creation.
        
        @author: Roy Nielsen

        :param domainTarget:  (Default value = "")
        :param servicePath:  (Default value = '')

        '''
        success = False
        cmd = ''
        #####
        # Input validation.
        if not isinstance(domainTarget, str) or \
           not isinstance(servicePath, str):
            return success

        if servicePath and domainTarget:
            cmd = {"bootstrap": [domainTarget, servicePath]}
        elif domainTarget:
            cmd = {"bootstrap": [domainTarget]}
        else:
            return success

        success, stdout, stderr, retcode = self.runSubCommand(cmd)

        if retcode != '0':
            self.logger.log(
                lp.DEBUG,
                reportStack() + "- success: " + str(success) + " stdout: " +
                str(stdout) + " stderr: " + str(stderr) + " retcode: " +
                str(retcode))
        return success

    # ----------------------------------------------------------------------

    def bootOut(self, domainTarget="", servicePath=''):
        '''@note: From the launchctl man page:
          bootstrap | bootout domain-target [service-path service-path2 ...] |
              service-target
              Bootstraps or removes domains and services. Services may be
              specified as a series of paths or a service identifier. Paths
              may point to XPC service bundles, launchd.plist(5) s, or a
              directories containing a collection of either. If there were
              one or more errors while bootstrapping or removing a collection
              of services, the problematic paths will be printed with the
              errors that occurred.
        
              If no paths or service target are specified, these commands can
              either bootstrap or remove a domain specified as a domain tar-
              get. Some domains will implicitly bootstrap pre-defined paths
              as part of their creation.
        
        @author: Roy Nielsen

        :param domainTarget:  (Default value = "")
        :param servicePath:  (Default value = '')

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(domainTarget, str) or \
           not isinstance(servicePath, str):
            return success

        if servicePath and domainTarget:
            cmd = {"bootout": [domainTarget, servicePath]}
        elif domainTarget:
            cmd = {"bootout": [domainTarget]}
        else:
            return success

        success, stdout, stderr, retcode = self.runSubCommand(cmd)
        #####
        # errors that indicate the process is complete or in
        # progress
        if re.search("No such process", stderr) or \
           re.search("Operation now in progress", stderr):
            success = True

        if retcode != '0' and not success:
            self.logger.log(
                lp.DEBUG,
                reportStack() + "- success: " + str(success) + " stdout: " +
                str(stdout) + " stderr: " + str(stderr) + " retcode: " +
                str(retcode))
        for item in stderr:
            if item and re.search("Could not find specified service", item):
                success = True
                break
        return success

    # ----------------------------------------------------------------------

    def enable(self, serviceTarget, servicePath=''):
        '''From the launchctl man page:
          enable | disable service-target
              Enables or disables the service in the requested domain. Once a
              service is disabled, it cannot be loaded in the specified
              domain until it is once again enabled. This state persists
              across boots of the device. This subcommand may only target
              services within the system domain or user and user-login
              domains.

        :param serviceTarget: 
        :param servicePath:  (Default value = '')

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(serviceTarget, str):
            return success

        if servicePath and isinstance(servicePath, str):
            cmd = {"enable": [serviceTarget, servicePath]}
        else:
            cmd = {"enable": [serviceTarget]}

        success, stdout, stderr, retcode = self.runSubCommand(cmd)
        if str(retcode) != '0':
            success = False
            self.logger.log(
                lp.DEBUG,
                reportStack() + "- success: " + str(success) + " stdout: " +
                str(stdout) + " stderr: " + str(stderr) + " retcode: " +
                str(retcode))
        else:
            self.logger.log(
                lp.DEBUG,
                reportStack() + "- success: " + str(success) + " stdout: " +
                str(stdout) + " stderr: " + str(stderr) + " retcode: " +
                str(retcode))
            success = True
        return success

    # -------------------------------------------------------------------------

    def disable(self, serviceTarget):
        '''From the launchctl man page:
          enable | disable service-target
              Enables or disables the service in the requested domain. Once a
              service is disabled, it cannot be loaded in the specified
              domain until it is once again enabled. This state persists
              across boots of the device. This subcommand may only target
              services within the system domain or user and user-login
              domains.

        :param serviceTarget: 

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(serviceTarget, str):
            return success

        cmd = {"disable": [serviceTarget]}
        success, stdout, stderr, retcode = self.runSubCommand(cmd)

        if str(retcode) != '0':
            success = False
            self.logger.log(
                lp.DEBUG,
                reportStack() + "- success: " + str(success) + " stdout: " +
                str(stdout) + " stderr: " + str(stderr) + " retcode: " +
                str(retcode))
        else:
            self.logger.log(
                lp.DEBUG,
                reportStack() + "- success: " + str(success) + " stdout: " +
                str(stdout) + " stderr: " + str(stderr) + " retcode: " +
                str(retcode))
            success = True
        return success

    #-------------------------------------------------------------------------

    def unCache(self, serviceName):
        '''Bypass the cache and read the service configuration from disk

        :param serviceName: 

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(serviceName, str):
            return success

        cmd = {"uncache": [serviceName]}
        success, stdout, stderr, retcode = self.runSubCommand(cmd)

        if str(retcode) != '0':
            success = False
            self.logger.log(
                lp.DEBUG,
                reportStack() + "- success: " + str(success) + " stdout: " +
                str(stdout) + " stderr: " + str(stderr) + " retcode: " +
                str(retcode))
        else:
            self.logger.log(
                lp.DEBUG,
                reportStack() + "- success: " + str(success) + " stdout: " +
                str(stdout) + " stderr: " + str(stderr) + " retcode: " +
                str(retcode))
            success = True
        return success

    # -------------------------------------------------------------------------

    def kickStart(self, serviceTarget="", options='-k'):
        '''From the launchctl man page:
          kickstart [-kp] service-target
              Instructs launchd to kickstart the specified service.
              Options can be one of:
        
              -k       If the service is already running, kill the running
                       instance before restarting the service.
        
              -p       Upon success, print the PID of the new process or the
                       already-running process to stdout.
        
            High sierra options:
              -s       Force the service to start.
        
              -x       Attach to xpcproxy(3) before it execs and becomes the
                       service process. This flag is generally not useful
                       for anyone but the launchd maintainer.
        
              (-p)     No longer available in High Sierra

        :param serviceTarget:  (Default value = "")
        :param options:  (Default value = '-k')

        '''
        #####
        # Input validation.
        args = []
        if re.match("[-kp]+", str(options)) and \
           isinstance(options, str):
            args.append(options)
        else:
            self.logger.log(lp.INFO,
                            "Need a the options to be a single " + "string...")

        args.append(serviceTarget)

        self.logger.log(lp.DEBUG, "args: " + str(args))

        cmd = {"kickstart": args}
        self.logger.log(lp.DEBUG, "cmd: " + str(cmd))
        success, stdout, stderr, retcode = self.runSubCommand(cmd)
        #####
        # If a '0' is returned
        if retcode == '0' and success:
            success = True
        else:
            raise ValueError("kickstart - success: " + str(success) +
                             " stdout: " + str(stdout) + " stderr: " +
                             str(stderr) + " retcode: " + str(retcode))

        return success

    # -------------------------------------------------------------------------

    def kill(self, signal="", serviceTarget=""):
        '''From the launchctl man page:
          kill signal-name | signal-number service-target
              Sends the specified signal to the specified service if it is
              running. The signal number or name (SIGTERM, SIGKILL, etc.) may
              be specified.

        :param signal:  (Default value = "")
        :param serviceTarget:  (Default value = "")

        '''
        success = False
        args = []
        #####
        # Validate signal - from the signal(3) manpage on OS X.
        signals = [
            'SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT',
            'SIGEMT', 'SIGFPE', 'SIGKILL', 'SIGBUS', 'SIGSEGV', 'SIGSYS',
            'SIGPIPE', 'SIGALRM', 'SIGTERM', 'SIGURG', 'SIGSTOP', 'SIGTSTP',
            'SIGCONT', 'SIGCHLD', 'SIGTTIN', 'SIGTTOU', 'SIGIO', 'SIGXCPU',
            'SIGXFSZ', 'SIGVTALRM', 'SIGPROF', 'SIGWINCH', 'SIGINFO',
            'SIGUSR1', 'SIGUSR2'
        ]
        if isinstance(signal, str) and signal in signals:
            args.append(signal)
        elif isinstance(signal, int) and signal < 32:
            args.append(signal)
        else:
            return success

        #####
        # Service target, just check for string...
        if isinstance(serviceTarget, str):
            args.append(serviceTarget)
        else:
            return success

        args.append(serviceTarget)

        cmd = {"kill": args}
        success, _, _, _ = self.runSubCommand(cmd)

        return success

    # -------------------------------------------------------------------------

    def blame(self, serviceTarget):
        '''From the launchctl man page:
          blame service-target
              If the service is running, prints a human-readable string
              describing why launchd launched the service. Note that services
              may run for many reasons; this subcommand will only show the
              most proximate reason. So if a service was run due to a timer
              firing, this subcommand will print that reason, irrespective of
              whether there were messages waiting on the service's various
              endpoints. This subcommand is only intended for debugging and
              profiling use and its output should not be relied upon in pro-
              duction scenarios.

        :param serviceTarget: 

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(serviceTarget, str):
            return success

        cmd = {"blame": [serviceTarget]}
        success, stdout, _, _ = self.runSubCommand(cmd)

        return success, stdout

    # -------------------------------------------------------------------------

    def printTarget(self, target):
        '''@note: From the launchctl man page:
          print domain-target | service-target
              Prints information about the specified service or domain.
              Domain output includes various properties about the domain as
              well as a list of services and endpoints in the domain with
              state pertaining to each. Service output includes various prop-
              erties of the service, including information about its origin
              on-disk, its current state, execution context, and last exit
              status.
        
              IMPORTANT: This output is NOT API in any sense at all. Do NOT
              rely on the structure or information emitted for ANY reason. It
              may change from release to release without warning.
        
        @author: Roy Nielsen

        :param target: 

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(target, str):
            return success

        # prepended system/ to service-target in order to hot fix multiple
        # issues with service detection in servicehelper two implementation
        # all rules calling new servicehelper must specify the service target
        # context  and they all currently do not. system/ is where all
        # system services run. currently servicehelper two cannot look for
        # user context services when being run in admin mode anyway, so this
        # is just a best-effort workaround until servicehelper two can
        # be redesigned or all the rules changed to prepend system/ in
        # their servicehelper calls
        cmd = {"print": ["system/" + target]}
        success, stdout, stderr, _ = self.runSubCommand(cmd)

        if re.search("Could not find service", stderr) and \
           re.search("in domain for system", stderr):
            success = False

        return success, stdout

    # -------------------------------------------------------------------------

    def printCache(self):
        '''@note: From the launchctl man page:
        print-cache
              Prints the contents of the launchd service cache.
        
        @author: Roy Nielsen


        '''
        cmd = {"print-cache": []}
        success, stdout, _, _ = self.runSubCommand(cmd)
        if success:
            self.logger.log(lp.DEBUG, str(success))
            self.logger.log(lp.DEBUG, str(stdout))

        return success, stdout

    # -------------------------------------------------------------------------

    def printDisabled(self, target=''):
        '''@note: From the launchctl man page:
          print-disabled
              Prints the list of disabled services.
        
        @author: Roy Nielsen

        :param target:  (Default value = '')

        '''
        success = False
        stdout = ''
        if target and isinstance(target, str):
            cmd = {"print-disabled": [target]}
            success, stdout, _, _ = self.runSubCommand(cmd)

        return success, stdout

    # -------------------------------------------------------------------------

    def procInfo(self, pid):
        '''@note: From the launchctl man page:
          procinfo pid
              Prints information about the execution context of the specified
              PID. This information includes Mach task-special ports and

        :param pid: 
        :raises what: names the ports are advertised as in the Mach bootstrap
        :raises namespace: if they are known to launchd
        :raises text.: This subcommand is intended for diagnostic purposes only
        :raises and: its output should not be relied upon in production scenar
        :raises ios.: This command requires root privileges
        :raises author: Roy Nielsen

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(pid, int):
            return success

        cmd = {"procinfo": [pid]}
        success, stdout, _, _ = self.runSubCommand(cmd)

        return success, stdout

    # -------------------------------------------------------------------------

    def hostinfo(self):
        '''@note: From the launchctl man page:
          hostinfo
              Prints information about the system's host-special ports,
              including the host-exception port. This subcommand requires
              root privileges.
        
        @author: Roy Nielsen


        '''
        cmd = {"hostinfo": []}
        _, stdout, _, _ = self.runSubCommand(cmd)

        return stdout

    # -------------------------------------------------------------------------

    def resolvePort(self, ownerPid, portName):
        '''@note: From the launchctl man page:
          resolveport owner-pid port-name
              Given a PID and the name of a Mach port right in that process'
              port namespace, resolves that port to an endpoint name known to
              launchd.  This subcommand requires root privileges.
        
        @author: Roy Nielsen

        :param ownerPid: 
        :param portName: 

        '''
        success = False
        #####
        # Input validation.
        if not isinstance(ownerPid, int) or not isinstance(portName, str):
            return success

        cmd = {"rsolveport": [ownerPid, portName]}
        _, stdout, _, _ = self.runSubCommand(cmd)

        return stdout

    # -------------------------------------------------------------------------

    def reboot(self, context, mountPoint):
        '''@note: From the launchctl man page:
          reboot [system|userspace|halt|logout|apps|reroot <mount-point>]
              Instructs launchd to begin tearing down userspace. With no
              argument given or with the system argument given, launchd will
              make the reboot(2) system call when userspace has been com-
              pletely torn down. With the halt argument given, launchd will
              make the reboot(2) system call when userspace has been com-
              pletely torn down and pass the RB_HALT flag, halting the system
              and not initiating a reboot.
        
              With the userspace argument given, launchd will re-exec itself
              when userspace has been torn down and bring userspace back up.
              This is useful for rebooting the system quickly under condi-
              tions where kernel data structures or hardware do not need to
              be re-initialized.
        
              With the reroot argument given, launchd will perform a
              userspace shutdown as with the userspace argument, but it will
              exec a copy of launchd from the specified mount-point.  This
              mechanism is a light-weight way of changing boot partitions. As
              part of this process, launchd will make mount-point the new
              root partition and bring userspace up as if the kernel had des-
              ignated mount-point as the root partition.
        
              IMPORTANT: This type of reboot will, in no way, affect the
              already-running kernel on the host. Therefore, when using this
              option to switch to another volume, you should only target vol-
              umes whose userspace stacks are compatible with the already-
              running kernel.
        
              NOTE: As of the date of this writing, this option does not com-
              pletely work.
        
              With the logout argument given, launchd will tear down the
              caller's GUI login session in a manner similar to a logout ini-
              tiated from the Apple menu. The key difference is that a logout
              initiated through this subcommand will be much faster since it
              will not give apps a chance to display modal dialogs to block
              logout indefinitely; therefore there is data corruption risk to
              using this option. Only use it when you know you have no
              unsaved data in your running apps.
        
              With the apps argument given, launchd will terminate all apps
              running in the caller's GUI login session that did not come
              from a launchd.plist(5) on-disk. Apps like Finder, Dock and
              SystemUIServer will be unaffected. Apps are terminated in the
              same manner as the logout argument, and all the same caveats
              apply.
        
              -s       When rebooting the machine (either a full reboot or
                       userspace reboot), brings the subsequent boot session
                       up in single-user mode.
        
        @author: Roy Nielsen

        :param context: 
        :param mountPoint: 

        '''
        success = False
        validContexts = ['System', 'users', 'halt', 'logout', 'apps', 'reroot']

        if not isinstance(context, str) or \
           not context in validContexts:
            return success
        if mountPoint and isinstance(mountPoint, str):
            cmd = {"reboot": [context, mountPoint]}
        elif not mountPoint:
            cmd = {"reboot": [context]}
        else:
            return success

        success, _, _, _ = self.runSubCommand(cmd)

        return success