Example #1
0
File: core.py Project: alttch/pptop
def format_shortcut(k):
    k = str(k)
    sh = k
    try:
        if k in key_names:
            sh = key_names[k]
        elif k.startswith('KEY_F('):
            fnkey = int(sh[6:-1])
            if fnkey > 48:
                sh = 'M-F{}'.format(fnkey - 48)
            elif fnkey > 24:
                sh = 'C-F{}'.format(fnkey - 24)
            elif fnkey > 12:
                sh = 'Sh-F{}'.format(fnkey - 12)
            else:
                sh = 'F{}'.format(fnkey)
        elif k.startswith('CTRL_'):
            sh = 'C-{}'.format(k[5:].lower())
        else:
            if len(k) == 1:
                if k.isalpha() and k.lower() != k:
                    sh = 'Sh-{}'.format(k.lower())
                elif k == ' ':
                    sh = 'Space'
                else:
                    sh = k
            else:
                sh = k.capitalize()
    except:
        log_traceback()
    return sh
Example #2
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)
Example #3
0
File: core.py Project: alttch/pptop
def update_status(**kwargs):
    try:
        _d.status = command('.status')
    except:
        log_traceback()
        status = -2
    finally:
        time.sleep(1)
Example #4
0
File: core.py Project: alttch/pptop
def grab_stdout(**kwargs):
    try:
        result = command('.gs')
        with stdout_buf_lock:
            _d.stdout_buf += result
    except:
        log_traceback()
    finally:
        time.sleep(0.5)
Example #5
0
def set_cursor(mode):
    if (term.startswith('screen') or not scr.active) and tput:
        try:
            code = os.system(tput + ' ' + ('civis' if not mode else 'cnorm'))
            if code:
                raise RuntimeError('tput error code: {}'.format(code))
            return
        except:
            log_traceback()
    try:
        curses.curs_set(mode)
    except:
        pass
Example #6
0
 def load_data(self):
     with self.data_lock:
         self.data.clear()
         try:
             for g in glob.glob(self.script_dir + '/**/*.py',
                                recursive=True):
                 d = OrderedDict()
                 script = g[len(self.script_dir) + 1:]
                 d['script'] = script
                 d['shortcut'] = self.script_hotkey_help.get(script, '')
                 self.data.append(d)
         except:
             log_traceback()
Example #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')
Example #8
0
File: core.py Project: alttch/pptop
def select_process():

    with scr.lock:
        cls()
        hide_cursor()
    selector = ProcesSelector(interval=1)
    selector.events = 0
    selector.name = 'process_selector'
    selector.sorting_rev = False
    selector.selectable = True
    selector.finish_event = threading.Event()
    selector.lock = threading.Lock()
    selector.title = 'Select process'
    selector.show()
    selector.start()
    _d.current_plugin = {'p': selector}
    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 event == 'back':
                selector.stop(wait=False)
                return
            elif event == 'filter':
                apply_filter(selector)
            elif event == 'select':
                if not selector.dtd:
                    continue
                selector.stop(wait=False)
                return psutil.Process(selector.dtd[selector.cursor]['pid'])
            elif event == 'pause':
                with scr.lock:
                    selector.toggle_pause()
            else:
                with scr.lock:
                    selector.key_code = k
                    selector.key_event = event
                    selector.trigger_threadsafe()
        except:
            log_traceback()
            raise
    return
Example #9
0
File: core.py Project: alttch/pptop
 def load_data(self):
     self.data.clear()
     px = ['python', 'python2', 'python3']
     user = getpass.getuser() if os.getuid() else 'root'
     for p in psutil.process_iter():
         try:
             with p.oneshot():
                 name = p.name().split('.', 1)[0]
                 fname = p.exe().rsplit('/', 1)[-1].split('.', 1)[0]
                 if (name in px
                         or fname in px) and p.pid != os.getpid() and (
                             user == 'root' or p.username() == user):
                     d = OrderedDict()
                     d['pid'] = p.pid
                     d['command line'] = ' '.join(p.cmdline())
                     self.data.append(d)
         except psutil.AccessDenied:
             pass
         except:
             log_traceback()
Example #10
0
    def load_data(self):
        '''
        Load plugin data

        Default method sends command cmd=<plugin_name>

        Returns:
            if False is returned, the plugin is stopped (doesn't works if
            self.background_loader=True)
        '''
        try:
            result = self.load_remote_data()
            processed = self.process_data(result)
            if isinstance(processed, list):
                result = processed
            if result is False or processed is False:
                return False
            if isinstance(result, list):
                d = result
            else:
                d = []
            with self.data_lock:
                if self.append_data:
                    self.data += d
                else:
                    self.data = d
                if self.data_records_max and len(
                        self.data) > self.data_records_max:
                    self.data = self.data[len(self.data) -
                                          self.data_records_max:]
            self._error = False
            self._display_ui()
            return True
        except Exception as e:
            log_traceback()
            self.data = []
            with scr.lock:
                self._error = True
                self.msg = e
                if self._visible:
                    self.print_title()
Example #11
0
File: core.py Project: 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
Example #12
0
 def run(self, **kwargs):
     '''
     Primary plugin executor method
     '''
     try:
         if (not self.key_event or self.key_event == 'reload'
             ) and not self._paused and not self._loader_active:
             self._loader_active = True
             if self.background_loader:
                 spawn(self._load_data)
                 return
             else:
                 if self._load_data() is False:
                     return False
         return self._display_ui()
     except Exception as e:
         log_traceback()
         if self._visible:
             with scr.lock:
                 self._error = True
                 self.msg = str(e)
                 self.print_title()
         return False
Example #13
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)
Example #14
0
File: core.py Project: 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()
Example #15
0
File: core.py Project: 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
Example #16
0
File: core.py Project: alttch/pptop
async def show_process_info(p, **kwargs):
    def error(txt):
        cls()
        scr.stdscr.addstr(0, 0, str(txt), palette.ERROR)
        scr.stdscr.refresh()
        return False

    def draw_val(row,
                 col,
                 label='',
                 value=None,
                 color=palette.DEFAULT,
                 spacer=True):
        width = _info_col_width[col]
        pos = _info_col_pos[col]
        val = str(value) if value is not None else ''
        scr.infowin.move(row + 1, pos)
        if label:
            scr.infowin.addstr(label)
            if spacer:
                scr.infowin.addstr(
                    ('.' if config['display']['colors'] else ' ') *
                    (width - len(label) - len(val)), palette.DARKGREY)
                scr.infowin.move(row + 1, pos + width - len(val))
            else:
                scr.infowin.addstr(' ')
        scr.infowin.addstr(val, color)

    try:
        width = scr.infowin.getmaxyx()[1]
        status = _d.status
        scr.infowin.clear()
        with p.oneshot():
            ct = p.cpu_times()
            memf = p.memory_full_info()
            mem = p.memory_info()
            ioc = p.io_counters()
            scr.infowin.move(0, 0)
            scr.infowin.addstr('Process: ')
            cmdline = format_cmdline(p, _d.need_inject_server)
            scr.infowin.addstr(cmdline[:width - 25], palette.YELLOW)
            scr.infowin.addstr(' [')
            scr.infowin.addstr(
                str(p.pid),
                palette.GREEN if status == 1 else palette.GREY_BOLD)
            scr.infowin.addstr(']')
            if status == -1:
                xst = 'WAIT'
                xstc = palette.GREY_BOLD
            elif status == 0:
                xst = 'DONE'
                xstc = palette.GREY_BOLD
            elif status == -2:
                xst = 'ERROR'
                xstc = palette.ERROR
            else:
                xst = None
            if xst:
                scr.infowin.addstr(' ' + xst, xstc)

            cpup = p.cpu_percent()

            draw_val(0, 0, 'CPU', '{}%'.format(cpup), palette.BLUE_BOLD)
            draw_val(1, 0, 'user', ct.user, palette.BOLD)
            draw_val(2, 0, 'system', ct.system, palette.BOLD)
            # always hide pptop thread
            draw_val(3, 0, 'threads', p.num_threads() - 1, palette.MAGENTA)

            # if config['display'].get('glyphs'):
            # gauge = _vblks[-1] * int(cpup // 25)
            # i = int(cpup % 25 / 25 * len(_vblks))
            # if i:
            # gauge += _vblks[i - 1]
            # x = _info_col_width[0] + 1
            # for i, g in enumerate(gauge):
            # scr.stdscr.addstr(4 - i, x, g * 2,
            # (palette.GREEN, palette.YELLOW,
            # palette.RED, palette.RED)[i])

            draw_val(0, 1, 'Memory uss', bytes_to_iso(memf.uss), palette.BOLD)
            draw_val(1, 1, 'pss', bytes_to_iso(memf.pss), palette.BOLD)
            draw_val(2, 1, 'swap', bytes_to_iso(memf.swap),
                     palette.GREY if memf.swap < 1000000 else palette.YELLOW)

            draw_val(0, 2, 'shd', bytes_to_iso(mem.shared), palette.BOLD)
            draw_val(1, 2, 'txt', bytes_to_iso(mem.text), palette.BOLD)
            draw_val(2, 2, 'dat', bytes_to_iso(mem.data), palette.BOLD)

            draw_val(0,
                     3,
                     'Files:',
                     len(p.open_files()),
                     palette.CYAN,
                     spacer=False)

            draw_val(1,
                     3,
                     value='{} {} ({})'.format(glyph.UPLOAD, ioc.read_count,
                                               bytes_to_iso(ioc.read_chars)),
                     color=palette.GREEN)
            draw_val(2,
                     3,
                     value='{} {} ({})'.format(glyph.DOWNLOAD, ioc.write_count,
                                               bytes_to_iso(ioc.write_chars)),
                     color=palette.BLUE)
        with scr.lock:
            scr.infowin.refresh()
            scr.stdscr.refresh()
    except psutil.AccessDenied:
        log_traceback()
        return error('Access denied')
    except psutil.NoSuchProcess:
        log_traceback()
        return error('Process is gone')
    except CriticalException:
        log_traceback()
        return error('Process server is gone')
    except curses.error:
        log_traceback()
        try:
            for i in range(2):
                scr.stdscr.move(i, 0)
                scr.stdscr.clrtoeol()
            scr.stdscr.refresh()
        except:
            pass
    except Exception as e:
        return error(e)
Example #17
0
File: core.py Project: 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()