Exemplo n.º 1
0
 def run_script(self, script, quiet=False):
     log('executing script {}'.format(script))
     self.inject()
     try:
         fname = '{}/{}'.format(self.script_dir, script)
         with open(fname) as fd:
             src = fd.read()
     except:
         log_traceback()
         if not quiet:
             self.print_message('Unable to load {}'.format(fname),
                                color=palette.ERROR)
         return
     try:
         result = self.injection_command(src=src)
     except Exception as e:
         result = e
     if not isinstance(result, Exception):
         if result is None:
             msg = '{} executed'.format(script)
         else:
             msg = '{}: {}'.format(script, result)
         if not quiet: self.print_message(msg, color=palette.GREEN_BOLD)
     else:
         if not quiet:
             self.print_message('{}: {}'.format(script, result),
                                color=palette.ERROR)
Exemplo n.º 2
0
Arquivo: core.py Projeto: alttch/pptop
 def autostart_plugins():
     for plugin in plugins_autostart:
         if plugin['p'] is not _d.current_plugin.get('p'):
             log('autostarting {}'.format(plugin['m']))
             inject_plugin(plugin)
             p = plugin['p']
             if p.background:
                 p.start()
Exemplo n.º 3
0
Arquivo: core.py Projeto: alttch/pptop
def inject_plugin(plugin):
    if plugin['p'].injected is False:
        log('injecting plugin {}'.format(plugin['p'].name))
        plugin['p'].injected = True
        try:
            command('.inject', plugin['i'])
            return True
        except:
            print_message('Plugin injection failed', color=palette.ERROR)
            return False
Exemplo n.º 4
0
Arquivo: core.py Projeto: alttch/pptop
def format_key(k):
    if len(k) == 1:
        z = ord(k)
        if z == 10:
            k = 'ENTER'
        elif z == 27:
            k = 'ESC'
        elif z < 27:
            k = 'CTRL_' + chr(z + 64)
    log('key pressed: {}'.format(k if len(k) > 1 else ((
        'ord=' + str(ord(k))) if ord(k) < 32 else '"{}"'.format(k))))
    return k
Exemplo n.º 5
0
def launch(cpid, wait=True, protocol=None):
    start(cpid, protocol=None, runner_mode=True)
    if wait is True:
        log('waiting for ready')
        while not g._runner_ready:
            time.sleep(0.2)
    elif wait > 0:
        log('waiting {} seconds'.format(wait))
        t_end = time.time() + wait
        while time.time() < t_end:
            if g._runner_ready:
                break
            time.sleep(0.1)
Exemplo n.º 6
0
def start(cpid, protocol=None, lg=None, runner_mode=False):
    if lg:
        init_logging(lg)
    else:
        stop_logging()
    log('starting injection server for pid {}'.format(cpid))
    if protocol and protocol <= PICKLE_PROTOCOL:
        protocol = protocol
    else:
        protocol = PICKLE_PROTOCOL
    t = threading.Thread(name='__pptop_injection_{}'.format(cpid),
                         target=loop,
                         args=(cpid, protocol, runner_mode))
    t.setDaemon(True)
    t.start()
Exemplo n.º 7
0
def main():
    import argparse
    ap = argparse.ArgumentParser()
    ap.add_argument('file', metavar='FILE', help='File to launch')
    ap.add_argument('cpid', metavar='PID', type=int, help='Client PID')
    ap.add_argument('-w',
                    '--wait',
                    metavar='SEC',
                    type=float,
                    help='Wait seconds till start')
    ap.add_argument('-p',
                    '--protocol',
                    metavar='VER',
                    type=int,
                    help='Pickle protocol')
    ap.add_argument('-a', '--args', metavar='ARGS', help='Child args (quoted)')
    ap.add_argument('--log', metavar='FILE', help='Send debug log to file')
    a = ap.parse_args()
    if a.protocol and a.protocol > PICKLE_PROTOCOL:
        raise ValueError('Protocol {} is not supported'.format(a.protocol))
    if a.log:
        init_logging(a.log)
    with open(a.file) as fh:
        src = fh.read()
    sys.argv = [a.file]
    if a.args:
        import shlex
        sys.argv += shlex.split(a.args)
    log('pptop injection runner started')
    launch(a.cpid, wait=True if a.wait is None else a.wait)
    g._runner_status = 1
    log('starting main code')
    try:
        code = compile(src, a.file, 'exec')
        launcher_g = {'__file__': a.file}
        exec(code, launcher_g)
        g._runner_status = 0
        log('main code finished')
    except:
        g._runner_status = -2
        log_traceback('exception in main code')
        e = sys.exc_info()
        with _g_lock:
            g._last_exception = (e[0].__name__, str(
                e[1]), [''])  # TODO: correct tb traceback.format_tb(e[2]))
    while not g._server_finished:
        time.sleep(0.2)
    # usually not executed as server kills process
    log('pptop injection runner stopped')
Exemplo n.º 8
0
 def on_load(self):
     self.title = 'Script runner'
     self.description = 'Run selected script from ~/.pptop/scripts'
     self.short_name = 'Script'
     self.sorting_rev = False
     self.selectable = True
     if 'script_dir' in self.config:
         self.script_dir = os.path.expanduser(self.config['script_dir'])
     else:
         self.script_dir = self.get_config_dir() + '/scripts'
     self.global_script_hotkeys = {}
     self.script_hotkey_help = {}
     script_keys = self.config.get('script-keys')
     if not script_keys:
         script_keys = {}
     for i, v in script_keys.items():
         for k in v if isinstance(v, list) else [v]:
             self.global_script_hotkeys[str(k)] = str(i)
             log('script hot key {} = {}'.format(k, i))
             if not os.path.isfile('{}/{}'.format(self.script_dir, i)):
                 log('WARNING: script {} doesn\'t exists'.format(i))
         self.script_hotkey_help[i] = ', '.join(
             [format_shortcut(x)
              for x in v]) if isinstance(v, list) else format_shortcut(v)
Exemplo n.º 9
0
Arquivo: core.py Projeto: alttch/pptop
def command(cmd, params=None):
    with client_lock:
        _d.client_frame_id += 1
        if _d.client_frame_id >= frame_counter_reset:
            _d.client_frame_id = 1
            _d.last_frame_id = 0
        try:
            frame = cmd.encode()
            if params is not None:
                frame += b'\xff' + pickle.dumps(params, protocol=_d.protocol)
            client.sendall(
                struct.pack('I', len(frame)) +
                struct.pack('I', _d.client_frame_id) + frame)
            time_start = time.time()
            data = client.recv(4)
            frame_id = struct.unpack('I', client.recv(4))[0]
        except:
            log_traceback()
            raise CriticalException('Injector is gone')
        if not data:
            log('critical: no data from injector')
            raise CriticalException('Injector error')
        l = struct.unpack('I', data)[0]
        data = b''
        while len(data) != l:
            data += client.recv(socket_buf)
            if time.time() > time_start + socket_timeout:
                raise CriticalException('Socket timeout')
        if frame_id != _d.client_frame_id:
            log('critical: got wrong frame, channel is broken')
            raise CriticalException('Wrong frame')
        _d.last_frame_id += 1
        with ifoctets_lock:
            _d.ifoctets += len(data) + 8
            if _d.ifoctets > 1000000000:
                _d.ifoctets = _d.ifoctets - 1000000000
        if data[0] != 0:
            log('injector command error, code: {}'.format(data[0]))
            raise RuntimeError('Injector command error')
        return pickle.loads(data[1:]) if len(data) > 1 else True
Exemplo n.º 10
0
Arquivo: core.py Projeto: alttch/pptop
def inject_server(gdb, p):
    cmds = []
    pid = p.pid
    libpath = os.path.abspath(os.path.dirname(__file__) + '/..')
    if _d.inject_method in ['native', 'loadcffi']:
        cmds.append('call (void)dlopen("{}", 2)'.format(_d.inject_lib))
    if _d.inject_method == 'native':
        cmds.append(
            'call (int)__pptop_start_injection("{}",{},{},"{}")'.format(
                libpath, os.getpid(), _d.protocol,
                log_config.fname if log_config.fname else ''))
    else:
        cmds += [
            'call (PyGILState_STATE)PyGILState_Ensure()',
            ('call (int)PyRun_SimpleString("' +
             'import sys\\nif \\"{path}\\" not in sys.path: ' +
             'sys.path.insert(0,\\"{path}\\")\\n' +
             'import pptop.injection;pptop.injection.start(' +
             '{mypid},{protocol}{lg})")').format(
                 path=libpath,
                 mypid=os.getpid(),
                 lg='' if not log_config.fname else ',lg=\\"{}\\"'.format(
                     log_config.fname),
                 protocol=_d.protocol), ' call (void)PyGILState_Release($1)'
        ]
    args = [gdb, '-p', str(pid), '--batch'
            ] + ['--eval-command={}'.format(c) for c in cmds]
    log(args)
    p = subprocess.Popen(args,
                         shell=False,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    out, err = p.communicate()
    log(out)
    log(err)
    if p.returncode:
        raise RuntimeError(err)
Exemplo n.º 11
0
def resize_handler(**kwargs):
    log('resize event')
    with scr.lock:
        resize_term()
Exemplo n.º 12
0
def loop(cpid, protocol, runner_mode=False):
    class STD:
        pass

    class ppStdout(object):

        write_through = False
        mode = 'w'

        def __init__(self, name, real, std):
            self.name = '<{}>'.format(name)
            self.real = real
            self.std = std
            try:
                self.encoding = real.encoding
            except:
                self.encoding = 'UTF-8'
            self.flush = real.flush
            self.isatty = real.isatty

        def writable(self):
            return True

        def write(self, text):
            with self.std.lock:
                self.std.buf += text
            return self.real.write(text)

        def writelines(self, lines):
            with self.std.lock:
                for l in lines:
                    self.std.buf += l
            return self.real.writelines(lines)

    def send_frame(conn, frame_id, data):
        conn.sendall(
            struct.pack('I', len(data)) + struct.pack('I', frame_id) + data)
        # log('{}: frame {}, {} bytes sent'.format(cpid, frame_id, len(data)))

    def send_serialized(conn, frame_id, data):
        send_frame(conn, frame_id,
                   b'\x00' + pickle.dumps(data, protocol=protocol))

    def send_ok(conn, frame_id):
        send_frame(conn, frame_id, b'\x00')

    def format_injection_unload_code(injection_id, src):
        return compile(u + '\ninjection_unload()',
                       '__pptop_injection_unload_' + injection_id, 'exec')

    server_address = '/tmp/.pptop.{}'.format(cpid)
    try:
        os.unlink(server_address)
    except:
        pass
    server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    server.bind(server_address)
    os.chmod(server_address, 0o600)
    server.listen(0)
    server.settimeout(socket_timeout)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, socket_buf)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, socket_buf)
    injections = {}
    real_stdout = None
    real_stderr = None
    log('Pickle protocol: {}'.format(protocol))
    log('listening')
    try:
        connection, client_address = server.accept()
        log('connected')
        connection.sendall(struct.pack('b', protocol))
        with _g_lock:
            g.clients += 1
        connection.settimeout(socket_timeout)
        exec_globals = {}
        while True:
            try:
                time_start = time.time()
                data = connection.recv(4)
                frame_id = struct.unpack('I', connection.recv(4))[0]
                if data:
                    l = struct.unpack('I', data)[0]
                    frame = b''
                    while len(frame) != l:
                        if time.time() > time_start + socket_timeout:
                            raise TimeoutError
                        frame += connection.recv(socket_buf)
                else:
                    break
            except:
                log_traceback('invalid data received or client is gone')
                break
            if frame:
                try:
                    cmd, params = frame.split(b'\xff', 1)
                    cmd = cmd.decode()
                    params = pickle.loads(params)
                except:
                    cmd = frame.decode()
                    params = {}
                try:
                    if cmd == '.test':
                        send_ok(connection, frame_id)
                    elif cmd == '.bye':
                        break
                    elif cmd == '.gs':
                        if real_stdout is None:
                            std = STD()
                            std.lock = threading.Lock()
                            std.buf = ''
                            with std.lock:
                                real_stdout = sys.stdout
                                real_stderr = sys.stderr
                                sys.stdout = ppStdout('stdout', real_stdout,
                                                      std)
                                sys.stderr = ppStdout('stderr', real_stderr,
                                                      std)
                                buf = ''
                        else:
                            with std.lock:
                                buf = std.buf
                                std.buf = ''
                        send_serialized(connection, frame_id, buf)
                    elif cmd == '.status':
                        send_serialized(connection, frame_id,
                                        g._runner_status if runner_mode else 1)
                    elif cmd == '.path':
                        send_serialized(connection, frame_id, sys.path)
                    elif cmd == '.x':
                        x = {}
                        try:
                            exec(params, x)
                            result = (0, x.get('out'))
                        except:
                            log_traceback()
                            e = sys.exc_info()
                            result = (1, e[0].__name__, str(e[1]))
                        send_serialized(connection, frame_id, result)
                    elif cmd == '.exec':
                        try:
                            if params.startswith('help'):
                                raise RuntimeError(
                                    'Help on remote is not supported')
                            if params.startswith('try: __result '):
                                src = params
                            else:
                                p1 = params.split(' ', 1)[0]
                                prfunc = '_print' if sys.version_info < (
                                    3, 0) else 'print'
                                if p1 in [
                                        'import', 'def', 'class', 'for',
                                        'while', 'raise', 'if', 'with', 'from',
                                        'try:'
                                ]:
                                    src = (
                                        'def {}(*args):\n' +
                                        ' __resultl.append(\' \'.join(str(a) '
                                        + 'for a in args))\n__resultl=[]\n{}' +
                                        '\n__result = \'\\n\'.join(__resultl) '
                                        + 'if __resultl else None').format(
                                            prfunc, params)
                                else:
                                    src = ('def {}(*args): ' +
                                           'return \' \'.join(str(a) ' +
                                           'for a in args)\n' +
                                           '__result = {}').format(
                                               prfunc, params)
                            exec(src, exec_globals)
                            result = exec_globals.get('__result')
                            try:
                                data = pickle.dumps(
                                    (0, safe_serialize(result)),
                                    protocol=protocol)
                            except:
                                log_traceback()
                                data = pickle.dumps((0, str(result)),
                                                    protocol=protocol)
                            send_frame(connection, frame_id, b'\x00' + data)
                        except:
                            log_traceback()
                            e = sys.exc_info()
                            with _g_lock:
                                g._last_exception = (e[0].__name__, str(e[1]),
                                                     [''])
                            send_serialized(connection, frame_id,
                                            (-1, e[0].__name__, str(e[1])))
                    elif cmd == '.le':
                        with _g_lock:
                            send_serialized(connection, frame_id,
                                            g._last_exception)
                    elif cmd == '.ready':
                        g._runner_ready = True
                        send_ok(connection, frame_id)
                    elif cmd == '.inject':
                        log(params)
                        injection_id = params['id']
                        if injection_id in injections:
                            u = injections[injection_id].get('u')
                            if u:
                                try:
                                    code = format_injection_unload_code(
                                        injection_id, u)
                                    exec(code, injections[injection_id]['g'])
                                    log('injection removed: {}'.format(
                                        injection_id))
                                except:
                                    log_traceback()
                        injections[injection_id] = {
                            'g': {
                                'g': SimpleNamespace(),
                                'mg': g
                            },
                            'u': params.get('u')
                        }
                        if 'l' in params:
                            code = compile(
                                params['l'] + '\ninjection_load(**load_kw)',
                                '__pptop_injection_load_' + injection_id,
                                'exec')
                            injections[injection_id]['g'][
                                'load_kw'] = params.get('lkw', {})
                            exec(code, injections[injection_id]['g'])
                        if 'i' in params:
                            src = params['i'] + '\n_r = injection(**kw)'
                        else:
                            src = '_r = None'
                        injections[injection_id]['i'] = compile(
                            src, '__pptop_injection_' + injection_id, 'exec')
                        log('injection completed: {}'.format(injection_id))
                        send_ok(connection, frame_id)
                    elif cmd in injections:
                        log('command {}, data: {}'.format(cmd, params))
                        gl = injections[cmd]['g']
                        gl['kw'] = params
                        exec(injections[cmd]['i'], gl)
                        send_serialized(connection, frame_id, gl['_r'])
                    else:
                        send_frame(connection, frame_id, b'\x01')
                except:
                    log_traceback()
                    send_frame(connection, frame_id, b'\x02')
            else:
                break
    except Exception as e:
        log_traceback()
    for i, v in injections.items():
        u = v.get('u')
        if u:
            try:
                code = format_injection_unload_code(i, u)
                exec(code, v['g'])
                log('injection removed: {}'.format(i))
            except:
                log_traceback()
    try:
        server.close()
    except:
        pass
    try:
        with _g_lock:
            g.clients -= 1
    except:
        pass
    if real_stdout is not None:
        sys.stdout = real_stdout
    if real_stderr is not None:
        sys.stderr = real_stderr
    try:
        os.unlink(server_address)
    except:
        pass
    log('finished')
    if runner_mode:
        g._server_finished = True
        os._exit(0)
Exemplo n.º 13
0
Arquivo: core.py Projeto: alttch/pptop
def start():
    def format_plugin_option(dct, o, v):
        if o.find('.') != -1:
            x, y = o.split('.', 1)
            dct[x] = {}
            format_plugin_option(dct[x], y, v)
        else:
            dct[o] = v

    _me = 'ppTOP version %s' % __version__

    ap = argparse.ArgumentParser(description=_me)
    ap.add_argument('-V',
                    '--version',
                    help='Print version and exit',
                    action='store_true')
    ap.add_argument('-R',
                    '--raw',
                    help='Raw mode (disable colors and unicode glyphs)',
                    action='store_true')
    ap.add_argument('--disable-glyphs',
                    help='disable unicode glyphs',
                    action='store_true')
    ap.add_argument('file',
                    nargs='?',
                    help='File, PID file or PID',
                    metavar='FILE/PID')
    ap.add_argument('-a', '--args', metavar='ARGS', help='Child args (quoted)')
    ap.add_argument('--python',
                    metavar='FILE',
                    help='Python interpreter to launch file')
    ap.add_argument('--gdb', metavar='FILE', help='Path to gdb')
    ap.add_argument(
        '-p',
        '--protocol',
        metavar='VER',
        type=int,
        help=textwrap.dedent('''Pickle protocol, default is highest.
                4: Python 3.4+,
                3: Python 3.0+,
                2: Python 2.3+,
                1: vintage'''))
    ap.add_argument('--inject-method',
                    choices=['auto', 'native', 'loadcffi', 'unsafe'],
                    help='Inject method')
    ap.add_argument('-g',
                    '--grab-stdout',
                    help='Grab stdout/stderr of injected process',
                    action='store_true')
    ap.add_argument(
        '-w',
        '--wait',
        metavar='SEC',
        type=float,
        help='If file is specified, wait seconds to start main code')
    ap.add_argument(
        '-f',
        '--config-file',
        help='Alternative config file (default: ~/.pptop/pptop.yml)',
        metavar='CONFIG',
        dest='config')
    ap.add_argument('-d',
                    '--default',
                    help='Default plugin to launch',
                    metavar='PLUGIN',
                    dest='plugin')
    ap.add_argument(
        '-o',
        '--plugin-option',
        help='Override plugin config option, e.g. threads.filter=mythread',
        metavar='NAME=VALUE',
        action='append',
        dest='plugin_options')
    ap.add_argument('--log', metavar='FILE', help='Send debug log to file')
    ap.add_argument('-x',
                    '--exec',
                    help='Exec code from a file ("-" for stdin) and exit '
                    ' (the code can put result to "out" var)',
                    metavar='FILE',
                    dest='_exec')
    ap.add_argument('-J',
                    '--json',
                    help='Output exec result as JSON',
                    action='store_true')

    try:
        import argcomplete
        argcomplete.autocomplete(ap)
    except:
        pass

    a = ap.parse_args()

    if a.log:
        log_config.fname = a.log
        log_config.name = 'client:{}'.format(os.getpid())
        logging.getLogger('asyncio').setLevel(logging.DEBUG)
        logging.getLogger('neotasker').setLevel(logging.DEBUG)
        logging.basicConfig(level=logging.DEBUG)
        le = logging.getLogger()
        le.addHandler(ppLoghandler())
        list(map(le.removeHandler, le.handlers))
        neotasker.set_debug(True)
        init_logging()

    if a.version:
        print(_me)
        exit()

    log('initializing')

    if a.file:
        try:
            # pid?
            _d.work_pid = int(a.file)
        except:
            # probably pid file
            try:
                with open(a.file) as fh:
                    _d.work_pid = int(fh.read(128))
            except:
                # okay, program to launch
                _d.child_cmd = os.path.abspath(a.file)

    _d.pptop_dir = os.path.expanduser('~/.pptop')

    if a.config:
        config_file = a.config
        use_default_config = False
    else:
        config_file = _d.pptop_dir + '/pptop.yml'
        use_default_config = True

    sys.path.append(_d.pptop_dir + '/lib')
    config.clear()
    if use_default_config and not os.path.isfile(config_file):
        log('no user config, setting default')
        try:
            os.mkdir(_d.pptop_dir)
        except:
            pass
        if not os.path.isdir(_d.pptop_dir + '/scripts'):
            shutil.copytree(dir_me + '/config/scripts',
                            _d.pptop_dir + '/scripts')
        shutil.copy(dir_me + '/config/pptop.yml', _d.pptop_dir + '/pptop.yml')
        if not os.path.isdir(_d.pptop_dir + '/lib'):
            os.mkdir(_d.pptop_dir + '/lib')
    with open(config_file) as fh:
        config.update(yaml.load(fh.read()))

    console = config.get('console')
    if console is None: console = {}

    _d.console_json_mode = console.get('json-mode')

    _d.inject_method = a.inject_method if a.inject_method else config.get(
        'inject-method')

    if config.get('display') is None:
        config['display'] = {}

    if a.raw:
        config['display']['colors'] = False

    if a.grab_stdout:
        _d.grab_stdout = True

    if a.raw or a.disable_glyphs:
        config['display']['glyphs'] = False

    if a._exec:
        if a._exec == '-':
            _d.exec_code = sys.stdin.read()
        else:
            with open(a._exec) as fd:
                _d.exec_code = fd.read()
        _d.output_as_json = a.json

    else:
        ebk = {}
        global_keys = config.get('keys')
        if global_keys:
            for event, keys in global_keys.items():
                for k, v in events_by_key.copy().items():
                    if event == v:
                        del events_by_key[k]
                if keys is not None:
                    for k in keys if isinstance(keys, list) else [keys]:
                        ebk[str(k)] = str(event)

        events_by_key.update(ebk)
        plugin_options = {}

        for x in a.plugin_options or []:
            try:
                o, v = x.split('=', 1)
            except:
                o = x
                v = None
            format_plugin_option(plugin_options, o, v)

        if plugin_options:
            config.update(merge_dict(config, {'plugins': plugin_options}))

        log('loading plugins')

        try:
            plugins.clear()
            for i, v in config.get('plugins', {}).items():
                try:
                    log('+ plugin ' + i)
                    if v is None: v = {}
                    try:
                        mod = importlib.import_module('pptop.plugins.' + i)
                        mod.__version__ = 'built-in'
                    except ModuleNotFoundError:
                        mod = importlib.import_module('pptopcontrib.' + i)
                        try:
                            mod.__version__
                        except:
                            raise RuntimeError(
                                'Please specify __version__ in plugin file')
                    plugin = {'m': mod}
                    plugins[i] = plugin
                    p = mod.Plugin(interval=float(
                        v.get('interval', mod.Plugin.default_interval)))
                    p.command = command
                    p.get_plugins = get_plugins
                    p.get_plugin = get_plugin
                    p.get_config_dir = get_config_dir
                    p.switch_plugin = switch_plugin
                    p.get_process = get_process
                    p.get_process_path = get_process_path
                    p.global_config = config
                    plugin['p'] = p
                    plugin['id'] = i
                    p._inject = partial(inject_plugin, plugin=plugin)
                    injection = {'id': i}
                    need_inject = False
                    try:
                        injection['l'] = inspect.getsource(mod.injection_load)
                        need_inject = True
                    except:
                        pass
                    try:
                        injection['i'] = inspect.getsource(mod.injection)
                        need_inject = True
                    except:
                        pass
                    try:
                        injection['u'] = inspect.getsource(
                            mod.injection_unload)
                        need_inject = True
                    except:
                        pass
                    if need_inject:
                        p.injected = False
                        plugin['i'] = injection
                    else:
                        p.injected = None
                    if not _d.default_plugin or val_to_boolean(
                            v.get('default')) or i == a.plugin:
                        _d.default_plugin = plugin
                    p_cfg = v.get('config')
                    p.config = {} if p_cfg is None else p_cfg
                    p.on_load()
                    p._on_load()
                    if 'l' in injection:
                        injection['lkw'] = p.get_injection_load_params()
                    if 'shortcut' in v:
                        sh = v['shortcut']
                        plugin['shortcut'] = sh
                        plugin_shortcuts[sh] = plugin
                        if sh.startswith('KEY_F('):
                            try:
                                f = int(sh[6:-1])
                                if f <= 10:
                                    bottom_bar_help[f] = p.short_name
                            except:
                                pass
                    else:
                        plugin['shortcut'] = ''
                    if 'filter' in v:
                        p.filter = str(v['filter'])
                    if 'cursor' in v:
                        p._cursor_enabled_by_user = val_to_boolean(v['cursor'])
                    if val_to_boolean(v.get('autostart')):
                        plugins_autostart.append(plugin)
                except Exception as e:
                    raise RuntimeError('plugin {}: {}'.format(i, e))
        except:
            log_traceback()
            raise
    neotasker.task_supervisor.start()
    neotasker.task_supervisor.create_aloop('pptop', default=True, daemon=True)
    neotasker.task_supervisor.create_aloop('service', daemon=True)
    try:
        if a.file and not _d.work_pid:
            # launch file
            _d.need_inject_server = False
            if a.python:
                python_path = a.python
            else:
                python_path = shutil.which('python3')
                if not python_path:
                    raise RuntimeError(
                        'python3 not found in path, please specify manually')
            args = (python_path, '-m', 'pptop.injection', a.file,
                    str(os.getpid()))
            if a.wait is not None:
                args += ('-w', str(a.wait))
            if a.protocol is not None:
                args += ('-p', str(a.protocol))
            if a.args:
                args += ('-a', a.args)
            if log_config.fname:
                args += ('--log', log_config.fname)
            log('starting child process')
            _d.child = subprocess.Popen(args,
                                        shell=False,
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.PIPE)
            _d.work_pid = _d.child.pid
            _d.protocol = pickle.HIGHEST_PROTOCOL
        else:
            if a.gdb:
                _d.gdb = a.gdb
            else:
                _d.gdb = shutil.which('gdb')
            if not _d.gdb or not os.path.isfile(_d.gdb):
                raise RuntimeError('gdb not found')
            # check yama ptrace scope
            try:
                with open('/proc/sys/kernel/yama/ptrace_scope') as fd:
                    yps = int(fd.read().strip())
            except:
                yps = None
            if yps:
                raise RuntimeError(
                    'yama ptrace scope is on. ' +
                    'disable with "sudo sysctl -w kernel.yama.ptrace_scope=0"')
            init_inject()
            log('inject method: {}'.format(_d.inject_method))
            log('inject library: {}'.format(_d.inject_lib))
            if a.protocol is not None:
                if a.protocol > pickle.HIGHEST_PROTOCOL or a.protocol < 1:
                    raise ValueError('Protocol {} is not supported'.format(
                        a.protocol))
                _d.protocol = a.protocol
                _d.force_protocol = a.protocol
            else:
                _d.protocol = pickle.HIGHEST_PROTOCOL
            log('Pickle protocol: {}'.format(_d.protocol))
        run()
        log('terminating')
        for p, v in plugins.items():
            v['p'].on_unload()
    except Exception as e:
        log_traceback()
        raise
    finally:
        try:
            client.close()
        except:
            pass
        neotasker.task_supervisor.stop(wait=False, cancel_tasks=True)
    return 0
Exemplo n.º 14
0
Arquivo: core.py Projeto: alttch/pptop
def run():
    def autostart_plugins():
        for plugin in plugins_autostart:
            if plugin['p'] is not _d.current_plugin.get('p'):
                log('autostarting {}'.format(plugin['m']))
                inject_plugin(plugin)
                p = plugin['p']
                if p.background:
                    p.start()

    try:

        if not _d.work_pid:
            init_curses(initial=True,
                        after_resize=after_resize,
                        colors=config['display'].get('colors'),
                        glyphs=config['display'].get('glyphs'))
            p = select_process()
        else:
            p = psutil.Process(_d.work_pid)

        if not p: return

        _d.process = p

        client.settimeout(socket_timeout)
        client.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, socket_buf)
        client.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, socket_buf)
        if _d.need_inject_server:
            inject_server(_d.gdb, p)
            log('server injected')
        sock_path = '/tmp/.pptop.{}'.format(os.getpid())
        for i in range(injection_timeout * 10):
            if os.path.exists(sock_path):
                break
            time.sleep(0.1)
        try:
            client.connect(sock_path)
        except:
            log_traceback()
            raise RuntimeError('Unable to connect to process')

        log('connected')

        frame = b''
        with client_lock:
            time_start = time.time()
            while len(frame) < 1:
                data = client.recv(1)
                if data:
                    frame += data
                    if time.time() > time_start + socket_timeout:
                        raise CriticalException('Socket timeout')

        server_protocol = struct.unpack('b', frame)[0]

        if server_protocol < _d.protocol:
            if _d.force_protocol:
                raise RuntimeError(
                    'Process doesn\'t support protocol {}'.format(_d.protocol))
            else:
                _d.protocol = server_protocol
                log('Falling back to protocol {}'.format(_d.protocol))

        if _d.exec_code:
            end_curses()
            result = command('.x', _d.exec_code)
            if result[0] == 0:
                if _d.output_as_json:
                    print_json(result[1])
                else:
                    print(result[1] if result[1] else '')
            else:
                print(err('{}: {}'.format(result[1], result[2])))
            return

        init_curses(initial=True,
                    after_resize=after_resize,
                    colors=config['display'].get('colors'),
                    glyphs=config['display'].get('glyphs'))

        signal.signal(signal.SIGWINCH, sigwinch_handler)

        calc_bw.start()
        update_status.start()
        if _d.grab_stdout:
            grab_stdout.start()

        _d.process_path.clear()
        plugin_process_path.clear()
        if _d.grab_stdout:
            try:
                command('.gs')
            except:
                raise RuntimeError('Unable to set stdout grabber')
        ppath = []
        for i in command('.path'):
            ppath.append(os.path.abspath(i))
        _d.process_path.extend(sorted(ppath, reverse=True))
        plugin_process_path.extend(_d.process_path)
        log('process path: {}'.format(_d.process_path))
        switch_plugin(_d.default_plugin)
        recalc_info_col_pos()
        show_process_info.start(p=p)
        show_bottom_bar.start()
        neotasker.spawn(autostart_plugins)
        log('main loop started')
        while True:
            try:
                try:
                    k = format_key(scr.stdscr.getkey())
                    event = get_key_event(k)
                except KeyboardInterrupt:
                    return
                except curses.error:
                    resize_handler.trigger_threadsafe(force=True)
                    continue
                if show_process_info.is_stopped():
                    return
                elif k in plugin_shortcuts:
                    switch_plugin(plugin_shortcuts[k])
                elif event == 'ready':
                    try:
                        result = command('.ready')
                    except:
                        result = None
                    with scr.lock:
                        if result:
                            print_message('Ready event sent', color=palette.OK)
                        else:
                            print_message('Command failed',
                                          color=palette.ERROR)
                elif event == 'reinject' and \
                        _d.current_plugin['p'].injected is not None:
                    try:
                        result = command('.inject', _d.current_plugin['i'])
                    except:
                        result = None
                    with scr.lock:
                        if result:
                            print_message('Plugin re-injected',
                                          color=palette.OK)
                        else:
                            print_message('Plugin re-injection failed',
                                          color=palette.ERROR)
                elif event == 'quit':
                    _d.current_plugin['p'].stop(wait=False)
                    show_process_info.stop(wait=False)
                    show_bottom_bar.stop(wait=False)
                    return
                elif event == 'console':
                    with scr.lock:
                        end_curses()
                        if _d.grab_stdout: print_stdout.start()
                        cli_mode()
                        if _d.grab_stdout: print_stdout.stop()
                        init_curses(after_resize=after_resize)
                        resize_term()
                elif event == 'show-console':
                    with scr.lock:
                        end_curses()
                        hide_cursor()
                        if _d.grab_stdout: print_stdout.start()
                        try:
                            wait_key()
                        except KeyboardInterrupt:
                            pass
                        if _d.grab_stdout: print_stdout.stop()
                        init_curses(after_resize=after_resize)
                        resize_term()
                elif event == 'filter':
                    apply_filter(_d.current_plugin['p'])
                elif event == 'interval':
                    apply_interval(_d.current_plugin['p'])
                elif event == 'pause':
                    with scr.lock:
                        _d.current_plugin['p'].toggle_pause()
                elif event in _d.current_plugin['p'].inputs:
                    with scr.lock:
                        try:
                            prev_value = _d.current_plugin['p'].get_input(
                                event)
                        except ValueError:
                            continue
                        value = prompt(
                            ps=_d.current_plugin['p'].get_input_prompt(event),
                            value=prev_value if prev_value is not None else '')
                        _d.current_plugin['p'].inputs[event] = value
                        try:
                            _d.current_plugin['p'].handle_input(
                                event, value, prev_value)
                        except:
                            pass
                else:
                    for i, plugin in plugins.items():
                        try:
                            plugin['p'].handle_key_global_event(event, k)
                        except:
                            log_traceback()
                    with scr.lock:
                        _d.current_plugin['p'].key_code = k
                        _d.current_plugin['p'].key_event = event
                        _d.current_plugin['p'].trigger_threadsafe()
            except:
                log_traceback()
                return
    except:
        log_traceback()
        raise
    finally:
        end_curses()
Exemplo n.º 15
0
def init_color_palette(force256=False):
    if term.endswith('256color') or force256:
        log('initializing terminal palette with 256 colors')
        palette.DARKGREY = curses.color_pair(238)
        palette.DARKGREY_BOLD = curses.color_pair(238) | curses.A_BOLD
        palette.DEBUG = curses.color_pair(244)
        palette.WARNING = curses.color_pair(187) | curses.A_BOLD
        palette.ERROR = curses.color_pair(198) | curses.A_BOLD
        palette.CRITICAL = curses.color_pair(197) | curses.A_BOLD
        palette.HEADER = curses.color_pair(37) | curses.A_REVERSE
        palette.CURSOR = curses.color_pair(32) | curses.A_REVERSE
        palette.BAR = curses.color_pair(37) | curses.A_REVERSE
        palette.BAR_OK = curses.color_pair(29) | curses.A_REVERSE
        palette.BAR_WARNING = curses.color_pair(4) | curses.A_REVERSE
        palette.BAR_ERROR = curses.color_pair(198) | curses.A_REVERSE
        palette.GREY = curses.color_pair(244)
        palette.GREY_BOLD = curses.color_pair(244) | curses.A_BOLD
        palette.GREEN = curses.color_pair(41)
        palette.GREEN_BOLD = curses.color_pair(41) | curses.A_BOLD
        palette.OK = curses.color_pair(121) | curses.A_BOLD
        palette.BLUE = curses.color_pair(40)
        palette.BLUE_BOLD = curses.color_pair(40) | curses.A_BOLD
        palette.RED = curses.color_pair(198)
        palette.RED_BOLD = curses.color_pair(198) | curses.A_BOLD
        palette.CYAN = curses.color_pair(51)
        palette.CYAN_BOLD = curses.color_pair(51) | curses.A_BOLD
        palette.MAGENTA = curses.color_pair(208)
        palette.MAGENTA_BOLD = curses.color_pair(208) | curses.A_BOLD
        palette.YELLOW = curses.color_pair(187)
        palette.YELLOW_BOLD = curses.color_pair(187) | curses.A_BOLD
        palette.WHITE_BOLD = curses.color_pair(231) | curses.A_BOLD
        palette.PROMPT = curses.color_pair(76) | curses.A_BOLD
    else:
        log('initializing terminal palette with 16 colors')
        palette.DARKGREY = curses.color_pair(1)
        palette.DARKGREY_BOLD = curses.color_pair(1) | curses.A_BOLD
        palette.DEBUG = curses.color_pair(1) | curses.A_BOLD
        palette.WARNING = curses.color_pair(4) | curses.A_BOLD
        palette.ERROR = curses.color_pair(2) | curses.A_BOLD
        palette.CRITICAL = curses.color_pair(2) | curses.A_BOLD
        palette.HEADER = curses.color_pair(3) | curses.A_REVERSE
        palette.CURSOR = curses.color_pair(7) | curses.A_REVERSE
        palette.BAR = curses.color_pair(7) | curses.A_REVERSE
        palette.BAR_OK = curses.color_pair(3) | curses.A_REVERSE
        palette.BAR_WARNING = curses.color_pair(4) | curses.A_REVERSE
        palette.BAR_ERROR = curses.color_pair(2) | curses.A_REVERSE
        palette.GREY = curses.color_pair(1)
        palette.GREY_BOLD = curses.color_pair(1) | curses.A_BOLD
        palette.GREEN = curses.color_pair(3)
        palette.GREEN_BOLD = curses.color_pair(3) | curses.A_BOLD
        palette.OK = curses.color_pair(3) | curses.A_BOLD
        palette.BLUE = curses.color_pair(5)
        palette.BLUE_BOLD = curses.color_pair(5) | curses.A_BOLD
        palette.RED = curses.color_pair(2)
        palette.RED_BOLD = curses.color_pair(2) | curses.A_BOLD
        palette.CYAN = curses.color_pair(7)
        palette.CYAN_BOLD = curses.color_pair(7) | curses.A_BOLD
        palette.MAGENTA = curses.color_pair(6)
        palette.MAGENTA_BOLD = curses.color_pair(6) | curses.A_BOLD
        palette.YELLOW = curses.color_pair(4)
        palette.YELLOW_BOLD = curses.color_pair(4) | curses.A_BOLD
        palette.WHITE_BOLD = curses.color_pair(8) | curses.A_BOLD
        palette.PROMPT = curses.color_pair(3) | curses.A_BOLD
Exemplo n.º 16
0
Arquivo: core.py Projeto: alttch/pptop
def cli_mode():
    def compl(text, state):
        if not text or text.find('.') == -1:
            return None
        o = text.rsplit('.', 1)[0]
        src = 'try: __result = dir({})\nexcept: pass'.format(o)
        result = command('.exec', src)
        if not result or result[0] or not result[1]: return None
        matches = [
            s for s in result[1] if ('{}.{}'.format(o, s)).startswith(text)
        ]
        try:
            return '{}.{}'.format(o, matches[state])
        except IndexError:
            return None

    log('cli mode started')
    if _d.cli_first_time:
        # os.system('clear')
        print(
            colored('Console mode, process {} connected'.format(
                _d.process.pid),
                    color='green',
                    attrs=['bold']))
        print(
            colored(format_cmdline(_d.process, _d.need_inject_server),
                    color='yellow'))
        print(
            colored(
                'Enter any Python command, press Ctrl-D or type "exit" to quit'
            ))
        print(colored('To toggle between JSON and normal mode, type ".j"'))
        if _d.grab_stdout:
            print(colored('To toggle stdout/stderr output, type ".p"'))
        print(
            colored(
                'To execute multiple commands from file, type "< filename"'))
        print(
            colored(
                'To explore object, type "obj?" (transformed to "dir(obj)")'))
        if _d.protocol < 3:
            print(
                colored('For Python 2 use \'_print\' instead of \'print\'',
                        color='yellow',
                        attrs=['bold']))
        print()
        _d.cli_first_time = False
    readline.set_history_length(100)
    readline.set_completer_delims('')
    readline.set_completer(compl)
    readline.parse_and_bind('tab: complete')
    try:
        readline.read_history_file('{}/console.history'.format(_d.pptop_dir))
    except:
        pass
    try:
        while True:
            try:
                cmd = input('>>> ').strip()
                if cmd == '': continue
                elif cmd == 'exit':
                    raise EOFError
                elif _d.grab_stdout and cmd == '.p':
                    if print_stdout.is_active():
                        print_stdout.stop()
                    else:
                        print_stdout.start()
                elif cmd == '.j':
                    _d.console_json_mode = not _d.console_json_mode
                    print('JSON mode ' +
                          ('on' if _d.console_json_mode else 'off'))
                else:
                    if cmd.startswith('<'):
                        with open(os.path.expanduser(cmd[1:].strip())) as fh:
                            cmds = filter(None,
                                          [x.strip() for x in fh.readlines()])
                    elif cmd.endswith('?'):
                        cmds = ['dir({})'.format(cmd[:-1]).strip()]
                    else:
                        cmds = [cmd]
                    for cmd in cmds:
                        r = command('.exec', cmd)
                        if r[0] == -1:
                            print(err('{}: {}'.format(r[1], r[2])))
                        else:
                            if r[1] is not None:
                                if _d.console_json_mode and \
                                        (isinstance(r[1], dict) or \
                                        isinstance(r[1], list)):
                                    print_json(r[1])
                                else:
                                    print(r[1])
            except EOFError:
                return
            except KeyboardInterrupt:
                print()
                continue
            except Exception as e:
                log_traceback()
                print(err(e))
    finally:
        log('cli mode completed')
        try:
            readline.write_history_file('{}/console.history'.format(
                _d.pptop_dir))
        except:
            log_traceback()
Exemplo n.º 17
0
Arquivo: core.py Projeto: alttch/pptop
def get_key_event(k):
    event = events_by_key.get(k, k)
    log('key event: {}'.format(event))
    return event
Exemplo n.º 18
0
Arquivo: core.py Projeto: alttch/pptop
 def emit(self, record):
     log(super().format(record))