X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=chiark-utils.git;a=blobdiff_plain;f=fishdescriptor%2Fpy%2Ffishdescriptor%2Findonor.py;h=12f8c685e01cf4651f5b53bd163acce4f9bfe2d1;hp=74c34a171a69f4950711e3c35fe31a7b94b3fcb8;hb=00ca8b27ab7bbea513959a13a93f98b1da14e2f9;hpb=16d5c02a1c894a2fa2cb31c6e3c916418ce71747 diff --git a/fishdescriptor/py/fishdescriptor/indonor.py b/fishdescriptor/py/fishdescriptor/indonor.py index 74c34a1..12f8c68 100644 --- a/fishdescriptor/py/fishdescriptor/indonor.py +++ b/fishdescriptor/py/fishdescriptor/indonor.py @@ -4,11 +4,12 @@ import gdb import copy import os +import sys def _string_escape_for_c(s): out = '' for c in bytearray(s): # gets us bytes in py2 and py3 - if c == ord('\\') or c == ord('=') or c < 32 or c > 126: + if c == ord('\\') or c == ord('"') or c < 32 or c > 126: out += '\\x%02x' % c else: out += chr(c) @@ -43,17 +44,25 @@ def _make_lit(v): else: return v # should already be an integer +def parse_eval(expr): + sys.stderr.write("## EVAL %s\n" % repr(expr)) + x = gdb.parse_and_eval(expr) + sys.stderr.write('## => %s\n' % x) + sys.stderr.flush() + return x + class DonorStructLayout(): def __init__(l, typename): - x = gdb.parse_and_eval('(%s){ }' % typename) + x = parse_eval('(%s){ }' % typename) l._typename = typename l._template = [ ] l._posns = { } for f in x.type.fields(): l._posns[f.name] = len(l._template) - try f.type.fields(): blank = '{ }' + try: f.type.fields(); blank = '{ }' except AttributeError: blank = '0' l._template.append(blank) + def substitute(l, values): build = copy.deepcopy(l._template) for (k,v) in values.items(): @@ -61,101 +70,161 @@ class DonorStructLayout(): return '((%s)%s)' % (l._typename, _lit_aggregate_uncasted(build)) class DonorImplementation(): - def __init__(d): - d._structs = { } + def __init__(di): + di._structs = { } + di._saved_errno = None + di._result_stream = os.fdopen(3, 'w') # assembling structs # sigh, we have to record the order of the arguments! - def d._find_fields(typename): + def _find_fields(typename): try: - fields = d._structs[typename] + fields = di._structs[typename] except AttributeError: fields = DonorStructLayout(typename) - d._structs[typename] = fields + di._structs[typename] = fields return fields - def d._make(typename, values): - fields = d._find_fields(typename) - return fieldd.substitute(values) + def _make(typename, values): + fields = di._find_fields(typename) + return fields.substitute(values) # calling functions (need to cast the function name to the right # type in case maybe gdb doesn't know the type) - def _func(d, functype, funcname, realargs): + def _func(di, functype, funcname, realargs): expr = '((%s) %s) %s' % (functype, funcname, realargs) - return gdb.parse_and_eval(expr) + return parse_eval(expr) - def _must_func(d, functype, funcname, realargs): - retval = d._func(functype, funcname, realargs) + def _must_func(di, functype, funcname, realargs): + retval = di._func(functype, funcname, realargs) if retval < 0: - errnoval = gdb.parse_and_eval('errno') + errnoval = parse_eval('errno') raise RuntimeError("%s gave errno=%d `%s'" % - (funcname, errnoval, od.strerror(errnoval))) + (funcname, errnoval, os.strerror(errnoval))) + return retval # wrappers for the syscalls that do what we want - def _sendmsg(d, carrier, control_msg): + def _sendmsg(di, carrier, control_msg): iov_base = _lit_array('int', map(str,fds)) - iov = d._make('struct iovec', { + iov = di._make('struct iovec', { 'iov_base': iov_base, 'iov_len' : len(fds), }) - msg = d._make('struct msghdr', { + msg = di._make('struct msghdr', { 'msg_iov' : _lit_addressof(iov), 'msg_iovlen' : 1, 'msg_control' : _lit_array('char', control_msg), 'msg_controllen': len(control_msg), }) - d._must_func( + di._must_func( 'ssize_t (*)(int, const struct msghdr*, int flags)', 'sendmsg', '(%s, %s, 0)' % (carrier, _lit_addressof(msg)) ) - def _socket(d): - return d._must_func( + def _socket(di): + return di._must_func( 'int (*)(int, int, int)', 'socket', '(%d, %d, 0)' % (socket.AF_UNIX, socket.SOCK_STREAM) ) - def _connect(d, fd, path): - addr = d._make('struct sockaddr_un', { + def _connect(di, fd, path): + addr = di._make('struct sockaddr_un', { 'sun_family' : _lit_integer(socket.AF_UNIX), 'sun_path' : _lit_string_uncasted(path), }) - d._must_func( + di._must_func( 'int (*)(int, const struct sockaddr*, socklen_t)', 'connect', '(%d, (const struct sockaddr*)%s, sizeof(struct sockaddr_un))' - % (fd, _lit_addressof(addr) + % (fd, _lit_addressof(addr)) ) - def _close(d, fd): - d._must_func('int (*)(int)', 'close', '(%d)' % fd) + def _close(di, fd): + di._must_func('int (*)(int)', 'close', '(%d)' % fd) + + def _mkdir(di, path, mode): + r = di._func( + 'int (*)(const char*, mode_t)', + 'mkdir', + '("%s", %d)' % (_string_escape_for_c(path), mode) + ) + if r < 0: + errnoval = parse_eval('errno') + if errnoval != os.errno.EEXIST: + raise RuntimeError("mkdir %s failed: `%s'" % + (repr(path), os.strerror(errnoval))) + return 0 + return 1 + + def _errno_save(di): + di._saved_errno = parse_eval('errno') + + def _errno_restore(di): + to_restore = di._saved_errno + di._saved_errno = None + if to_restore is not None: + parse_eval('errno = %d' % to_restore) + + def _result(di, output): + sys.stderr.write("#> %s" % output) + di._result_stream.write(output) + di._result_stream.flush() # main entrypoints - def donate(d, path, control_msg): + def donate(di, path, control_msg): # control_msg is an array of integers being the ancillary data # array ("control") for sendmsg, and hence specifies which fds # to pass carrier = None - errnoval = None try: - errnoval = gdb.parse_and_eval('errno') - carrier = d._socket() - d._connect(carrier, path) - d._sendmsg(carrier, control_msg) - d._close(carrier) + di._errno_save() + carrier = di._socket() + di._connect(carrier, path) + di._sendmsg(carrier, control_msg) + di._close(carrier) carrier = None finally: if carrier is not None: - try: d._close(carrier) + try: di._close(carrier) except Exception: pass - if errnoval is not None: - gdb.parse_and_eval('errno = %d' % errnoval) + di._errno_restore() + + di._result('1\n') + + def geteuid(di): + try: + di._errno_save() + val = di._must_func('uid_t (*)(void)', 'geteuid', '()') + finally: + di._errno_restore() + + di._result('%d\n' % val) + + def mkdir(di, path): + try: + di._errno_save() + val = di._mkdir(path, int('0700', 8)) + finally: + di._errno_restore() + + di._result('%d\n' % val) + + def _protocol_read(di): + input = sys.stdin.readline().rstrip('\n') + sys.stderr.write("#< %s\n" % input) + return input + + def eval_loop(di): + while True: + di._result('!\n') + cmd = di._protocol_read() + eval(cmd)