class LinuxDevice(DesktopDevice):
    def __init__(self, config, connector):
        super().__init__(config, connector)
        self._connector_helper = ConnectorHelper(self)

    @staticmethod
    def local_ips():
        raise XVEx("TODO: Local IPs for Linux")

    @staticmethod
    def open_app(binary_path, root=False):
        unused(root)
        if binary_path is None:
            L.debug('Application has no binary path; not opening')
        # TODO: open the application here

    @staticmethod
    def close_app(binary_path, root=False):
        unused(root)
        if binary_path is None:
            L.debug('Application has no binary path; not closing')
        # TODO: close the application here

    def os_name(self):
        return 'linux'

    def os_version(self):
        return " ".join(platform.linux_distribution())

    def report_info(self):
        info = super().report_info()
        commands = [
            ['uname', '-a'],
            ['lsb_release', '-a'],
            ['lscpu'],
        ]
        for command in commands:
            try:
                info += self._connector_helper.check_command(command)[0]
            except XVProcessException as ex:
                L.warning(
                    "Couldn't get system info using command {}:\n{}".format(
                        command, ex))

        return info

    def kill_process(self, pid):
        L.debug("Killing process {}".format(pid))
        return self._connector_helper.execute_scriptlet(
            'remote_os_kill.py', [pid, int(signal.SIGKILL)], root=True)

    def pgrep(self, process_name):
        L.debug("pgrep-ing for {}".format(process_name))
        return self._connector_helper.execute_scriptlet('pgrep.py',
                                                        [process_name],
                                                        root=True)

    def command_line_for_pid(self, pid):
        return self._connector_helper.execute_scriptlet(
            'command_line_for_pid.py', [pid], root=True)
class LinuxDevice(DesktopDevice):
    def __init__(self, config, connector):
        super().__init__(config, connector)
        self._connector_helper = ConnectorHelper(self)

    @staticmethod
    def local_ips():
        raise XVEx("TODO: Local IPs for Linux")

    @staticmethod
    def open_app(binary_path, root=False):
        unused(root)
        if binary_path is None:
            L.debug('Application has no binary path; not opening')
        # TODO: open the application here

    @staticmethod
    def close_app(binary_path, root=False):
        unused(root)
        if binary_path is None:
            L.debug('Application has no binary path; not closing')
        # TODO: close the application here

    def os_name(self):
        return 'linux'

    def os_version(self):
        L.warning("TODO: Linux version")
        return 'TODO: Linux version'

    def report_info(self):
        # TODO: Add Linux-specific reporting here.
        info = super().report_info()
        return info

    def kill_process(self, pid):
        L.debug("Killing process {}".format(pid))
        return self._connector_helper.execute_scriptlet(
            'remote_os_kill.py', [pid, int(signal.SIGKILL)], root=True)

    def pgrep(self, process_name):
        L.debug("pgrep-ing for {}".format(process_name))
        return self._connector_helper.execute_scriptlet('pgrep.py',
                                                        [process_name],
                                                        root=True)

    def command_line_for_pid(self, pid):
        return self._connector_helper.execute_scriptlet(
            'command_line_for_pid.py', [pid], root=True)
Esempio n. 3
0
class MacOSDevice(DesktopDevice):

    def __init__(self, config, connector):
        super().__init__(config, connector)
        # TODO: I think this should be part of DesktopDevice. Need to clarify what all these thigns
        # mean. I think we should move to DesktopDevice meaning anything with the tools. Maybe even
        # this becomes ToolsDevice.
        self._connector_helper = ConnectorHelper(self)

    # TODO: This needs to execute remotely in general. Let's make a scriptlet. Let's ensure that
    # nothing on the device classes themselves restricts the devices to being the localhost
    @staticmethod
    def local_ips():
        ips = []
        for iface in netifaces.interfaces():
            if netifaces.AF_INET in netifaces.ifaddresses(iface):
                ips.append(netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr'])
        return [ipaddress.ip_address(ip) for ip in ips]

    def open_app(self, bundle_path, root=False):
        # Quote the bundle path as some have spaces in
        self._connector_helper.execute_scriptlet(
            'macos_open_app.py', ["'{}'".format(bundle_path)], root=root)

    def close_app(self, bundle_path, root=False):
        # Quit by sending quit signal to the window so the app shuts down how a user would shut it
        # down. In theory it's equivalent to a pkill but slightly more realistic this way
        self._connector_helper.execute_command(
            ['osascript', '-e', "'quit app \"{}\"'".format(bundle_path)], root=root)

    def os_name(self):
        return 'macos'

    def os_version(self):
        return self._connector_helper.execute_scriptlet('remote_mac_ver.py', [])[0]

    def report_info(self):
        info = super().report_info()
        try:
            info += self._connector_helper.check_command(
                ['system_profiler', 'SPSoftwareDataType'])[0]
        except XVProcessException as ex:
            L.warning("Couldn't get OS info from system_profiler:\n{}".format(ex))

        return info

    def kill_process(self, pid):
        L.debug("Killing process {}".format(pid))
        return self._connector_helper.execute_scriptlet(
            'remote_os_kill.py', [pid, int(signal.SIGKILL)], root=True)

    def pgrep(self, process_name):
        '''Similar to the posix pgrep program, however it will return any process ids where
        process_name is a a substring of the whole process command line.'''
        L.debug("pgrep-ing for {}".format(process_name))
        return self._connector_helper.execute_scriptlet('pgrep.py', [process_name], root=True)

    def command_line_for_pid(self, pid):
        return self._connector_helper.execute_scriptlet('command_line_for_pid.py', [pid], root=True)
Esempio n. 4
0
def main(argv=None):
    if argv is None:
        argv = sys.argv[1:]

    scriptlet_args = argv[1:]
    argv = argv[0:1]

    parser = argparse.ArgumentParser(description='Execute a scriptlet locally for testing')
    parser.add_argument(
        'scriptlet', help='Name of the scriptlet in the xv_leak_tools/scriplets folder')

    args = parser.parse_args(argv)

    L.configure({
        'trace': {
            'level': L.INFO,
        },
        'describe': {
            'file_format': None,
        },
        'report': {
            'file_format': None,
        },
    })

    try:
        L.info("Running scriptlet {} with args {}".format(args.scriptlet, scriptlet_args))

        context = TestRunContext({'output_directory': 'output'})
        localhost = LocalhostDiscoverer(context, []).discover_device({})
        helper = ConnectorHelper(localhost)
        L.info("scriptlet returned {}".format(helper.execute_scriptlet(
            'remote_mac_ver.py', scriptlet_args)))

    except BaseException as ex:
        L.info("scriptlet raised {}".format(ex))

    return 0
class WindowsDevice(DesktopDevice):  # pylint: disable=no-self-use

    PROG_OS_VERSION = re.compile(r'OS Version:\s*([^\s]+).*')

    def __init__(self, config, connector):
        super().__init__(config, connector)
        self._connector_helper = ConnectorHelper(self)

    def _pgrep_cygwin(self, process_name):
        # -W shows windows processes under cygwin
        lines = self._connector_helper.check_command(['ps',
                                                      '-efW'])[0].splitlines()
        lines = [line for line in lines if process_name in line]
        return [int(line.split()[1]) for line in lines]

    def is_cygwin(self):
        ret = self._connector_helper.execute_command(['uname'])[0]
        # Don't even need to check the output. This will fail if DOS.
        return ret == 0

    def os_name(self):
        return 'windows'

    def os_version(self):
        try:
            # Use systeminfo on Windows as platform.win32_ver doesn't work for cygwin. That way the
            # code is agnostic to the shell.
            output = self._connector_helper.check_command(['systeminfo'])[0]
            for line in output.splitlines():
                match = WindowsDevice.PROG_OS_VERSION.match(line)
                if match:
                    return match.group(1)
            raise XVEx(
                "Couldn't determine Windows Version from systeminfo output:\n{}"
                .format(output))
        except XVProcessException as ex:
            raise XVEx(
                "Couldn't determine Windows Version as systeminfo failed:\n{}".
                format(ex))

    # TODO: I think all desktop devices share this, so derive instead.
    @staticmethod
    def local_ips():
        ips = []
        for iface in netifaces.interfaces():
            if netifaces.AF_INET in netifaces.ifaddresses(iface):
                ips.append(
                    netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr'])
        return [ipaddress.ip_address(ip) for ip in ips]

    # TODO: Not sure this will either work at all or work on cygwin
    def open_app(self, app_path, root=False):  # pylint: disable=unused-argument
        cmd = ['run', "\"{}\"".format(windows_safe_path(app_path))]
        L.debug("Executing cmd '{}'".format(cmd))
        self._connector_helper.check_command(cmd)

    def close_app(self, app_path, root=False):  # pylint: disable=unused-argument
        pname = windows_path_split(app_path)[1]
        pids = [str(pid) for pid in self.pgrep(pname)]
        if len(pids) > 1:
            L.warning(
                "Closing all pids {} associated to application {}".format(
                    ', '.join(pids), pname))
        for pid in pids:
            self.kill_process(pid)

    def kill_process(self, pid):
        L.debug("Killing process {}".format(pid))
        # taskkill is the most generic way to handle windows
        self._connector_helper.check_command(
            ['taskkill', '/PID', str(pid), '/F'], root=True)

    def pgrep(self, process_name):
        '''Similar to the posix pgrep program, however it will return any process ids where
        process_name is a a substring of the whole process command line.'''
        L.debug("pgrep-ing for {}".format(process_name))
        if self.is_cygwin():
            return self._pgrep_cygwin(process_name)
        return self._connector_helper.execute_scriptlet('pgrep.py',
                                                        [process_name],
                                                        root=True)

    # This is pretty sketchy. Do better!
    @staticmethod
    def _fix_quotes(cmd_line):
        args = []
        next_arg = ""
        start_quote = False
        escaping = False
        for ichar, char in enumerate(cmd_line):
            if char not in ["\\", "\"", " "]:
                next_arg += char
                continue
            if char == "\"":
                if escaping:
                    next_arg += char
                    escaping = False
                    continue
                elif start_quote:
                    args.append(next_arg)
                    start_quote = False
                    next_arg = ""
                    continue
                else:
                    start_quote = True
                    continue
            elif char == "\\":
                if cmd_line[ichar + 1] == "\"":
                    escaping = True
                    next_arg += char
                else:
                    next_arg += char
                    continue
            elif char == " ":
                if start_quote:
                    next_arg += char
                    continue
                else:
                    if next_arg:
                        args.append(next_arg)
                        next_arg = ""
                    continue
        if next_arg != "":
            args.append(next_arg)
        return args

    def command_line_for_pid(self, pid):
        if self.is_cygwin():
            # The psutil module isn't supported on cygwin
            cmd = [
                "wmic", "process", "where", "ProcessID='{}'".format(pid),
                "get", "CommandLine"
            ]
            args = self._connector_helper.check_command(cmd)[0]
            L.verbose("Raw wmic command line was: {}".format(args))
            return WindowsDevice._fix_quotes(args)

        return self._connector_helper.execute_scriptlet(
            'command_line_for_pid.py', [pid])

    def report_info(self):
        info = super().report_info()
        try:
            info += self._connector_helper.check_command(['systeminfo'])[0]
        except XVProcessException as ex:
            L.warning("Couldn't get OS info from systeminfo:\n{}".format(ex))

        return info