├── .gitignore ├── LICENSE.md ├── MANIFEST.in ├── README.md ├── requirements.txt ├── rpudb.py-old ├── rpudb └── __init__.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | venv/ 4 | build/ 5 | dist/ 6 | MANIFEST 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Marcelo Salhab Brogliato 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | remote pudb 2 | =========== 3 | Runs a telnet server and allows controlling the pudb from a remote computer. 4 | 5 | 6 | How to install 7 | -------------- 8 | 9 | pip install rpudb 10 | 11 | 12 | How to use 13 | ---------- 14 | In the server: 15 | 16 | import rpudb 17 | rpudb.set_trace(addr='0.0.0.0', port=4444) 18 | 19 | In your computer: 20 | 21 | telnet localhost 4444 22 | 23 | 24 | How it works 25 | ------------ 26 | It runs pudb in a pseudo-terminal. You connect to this pseudo-terminal through a telnet server. 27 | 28 | 29 | License 30 | ------- 31 | Released under the MIT license. Read LICENSE.md for more information. 32 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pudb 2 | -------------------------------------------------------------------------------- /rpudb.py-old: -------------------------------------------------------------------------------- 1 | import pudb 2 | import sys 3 | import socket 4 | import struct 5 | import fcntl 6 | import termios 7 | import pty 8 | 9 | def set_trace(addr='127.0.0.1', port=4444): 10 | # Backup stdin and stdout before replacing them by the socket handle 11 | old_stdout = sys.stdout 12 | old_stdin = sys.stdin 13 | 14 | # Open a 'reusable' socket to let the webapp reload on the same port 15 | skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 16 | skt.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) 17 | skt.bind((addr, port)) 18 | skt.listen(1) 19 | 20 | # Writes to stdout are forbidden in mod_wsgi environments 21 | try: 22 | sys.stderr.write("pudb is running on %s:%d\n" % skt.getsockname()) 23 | except IOError: 24 | pass 25 | 26 | (clientsocket, address) = skt.accept() 27 | handle = clientsocket.makefile('rw') 28 | 29 | (master, slave) = pty.openpty() 30 | 31 | width = 143 32 | height = 39 33 | winsize = struct.pack("HHHH", height, width, 0, 0) 34 | fcntl.ioctl(slave, termios.TIOCSWINSZ, winsize) 35 | 36 | buf = fcntl.ioctl(slave, termios.TIOCGWINSZ, ' '*4) 37 | y, x = struct.unpack('hh', buf) 38 | print '-->', width, height, x, y 39 | 40 | return handle 41 | sys.stdout = sys.stdin = handle 42 | 43 | # Telnet Linemode Option 44 | # http://tools.ietf.org/html/rfc1184 45 | # IAC WONT LINEMODE IAC WILL ECHO 46 | sys.stdout.write(bytearray([0377, 0375, 042, 0377, 0373, 01])) 47 | sys.stdout.flush() 48 | 49 | pudb.set_trace() 50 | 51 | if __name__ == '__main__': 52 | set_trace() 53 | -------------------------------------------------------------------------------- /rpudb/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import socket 3 | import struct 4 | import fcntl 5 | import termios 6 | import pty 7 | import os 8 | import select 9 | import sys 10 | import signal 11 | import atexit 12 | 13 | import pudb 14 | 15 | def run_telnet_server(addr, port): 16 | # Open a 'reusable' socket to let the webapp reload on the same port 17 | skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 18 | skt.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) 19 | skt.bind((addr, port)) 20 | skt.listen(1) 21 | 22 | # Writes to stdout are forbidden in mod_wsgi environments 23 | try: 24 | sys.stderr.write("pudb is running on %s:%d\n" % skt.getsockname()) 25 | except IOError: 26 | pass 27 | 28 | (clientsocket, address) = skt.accept() 29 | 30 | # Create pseudo-terminal 31 | (term_master, term_slave) = pty.openpty() 32 | # Create file descriptor for stdin 33 | (stdin_master, stdin_slave) = os.pipe() 34 | 35 | # Telnet control characters (http://www.cs.cf.ac.uk/Dave/Internet/node141.html) 36 | SE = 240 37 | SB = 250 38 | WILL = 251 39 | WONT = 252 40 | DO = 253 41 | DONT = 254 42 | IAC = 255 43 | ECHO = 1 44 | SUPPRESS_GO_AHEAD = 3 45 | LINEMODE = 34 46 | NAWS = 31 47 | 48 | # Telnet Linemode Option (http://tools.ietf.org/html/rfc1184) 49 | clientsocket.sendall(bytearray([IAC, WILL, ECHO, IAC, WILL, SUPPRESS_GO_AHEAD, IAC, WONT, LINEMODE])) 50 | buf = clientsocket.recv(3) 51 | buf = struct.unpack('BBB', buf) 52 | assert(buf == (IAC, DO, ECHO)) 53 | buf = clientsocket.recv(3) 54 | buf = struct.unpack('BBB', buf) 55 | assert(buf == (IAC, DO, SUPPRESS_GO_AHEAD)) 56 | 57 | # Telnet Window Size Option (https://www.ietf.org/rfc/rfc1073.txt) 58 | clientsocket.sendall(bytearray([IAC, DO, NAWS])) 59 | buf = clientsocket.recv(3) 60 | buf = struct.unpack('BBB', buf) 61 | assert(buf == (IAC, WILL, NAWS)) 62 | 63 | pid = os.fork() 64 | if pid == 0: 65 | strIAC = struct.pack('BBB', IAC, SB, NAWS) 66 | inputs = [clientsocket, term_master] 67 | print('Mainloop') 68 | while True: 69 | readable, _, _ = select.select(inputs, [], []) 70 | s = readable[0] 71 | if s == term_master: 72 | buf = os.read(term_master, 1000) 73 | clientsocket.sendall(buf) 74 | 75 | elif s == clientsocket: 76 | buf = clientsocket.recv(1000) 77 | idx = buf.find(strIAC) 78 | if idx >= 0: 79 | # Window size: IAC SB NAWS 0 80 0 24 IAC SE 80 | cmd = struct.unpack('BBBBBBBBB', buf[idx:idx+9]) 81 | buf = buf[:idx] + buf[idx+9:] 82 | assert(cmd[3] == 0) 83 | assert(cmd[5] == 0) 84 | assert(cmd[7:] == (IAC, SE)) 85 | width = cmd[4] 86 | height = cmd[6] 87 | #print 'Window size:', width, height 88 | winsize = struct.pack("HHHH", height, width, 0, 0) 89 | fcntl.ioctl(term_master, termios.TIOCSWINSZ, winsize) 90 | if len(buf) > 0: 91 | assert(os.write(stdin_slave, buf) == len(buf)) 92 | 93 | return pid, os.fdopen(term_slave, 'w'), os.fdopen(stdin_master, 'r') 94 | 95 | def set_trace(addr='127.0.0.1', port=4444): 96 | # Backup stdin and stdout before replacing them by the socket handle 97 | old_stdout = sys.stdout 98 | old_stdin = sys.stdin 99 | 100 | # Run telnet server and get the new fds 101 | (pid, stdout, stdin) = run_telnet_server(addr, port) 102 | sys.stdout = stdout 103 | sys.stdin = stdin 104 | 105 | # Kill children on exit. 106 | def cleanup(): 107 | print('Killing server...') 108 | os.kill(pid, signal.SIGKILL) 109 | atexit.register(cleanup) 110 | def signal_handler(signal, frame): 111 | sys.exit(0) 112 | signal.signal(signal.SIGINT, signal_handler) 113 | 114 | # Finally, run pudb 115 | pudb.set_trace() 116 | 117 | 118 | if __name__ == '__main__': 119 | set_trace() 120 | 121 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | setup(name='rpudb', 4 | version='0.0.3', 5 | license='MIT', 6 | author='Marcelo Salhab Brogliato', 7 | author_email='msbrogli@vialink.com.br', 8 | description='Remote pudb', 9 | long_description=open('README.md').read(), 10 | packages=['rpudb'], 11 | url='https://github.com/msbrogli/rpudb', 12 | install_requires=['pudb'], 13 | ) 14 | --------------------------------------------------------------------------------