2 # class for use inside gdb which is debugging the donor process
8 def _string_escape_for_c(s):
10 for c in bytearray(s): # gets us bytes in py2 and py3
11 if c == ord('\\') or c == ord('"') or c < 32 or c > 126:
22 def _lit_aggregate_uncasted(val_lit_strs):
23 return '{' + ', '.join(['(%s)' % v for v in val_lit_strs]) + ' }'
25 def _lit_string_uncasted(s):
26 if not isinstance(s, bytes):
27 s = s.encode('utf-8') # sigh, python 2/3 compat
29 return _lit_aggregate_uncasted(map(_lit_integer, b) + [ '0' ])
31 def _lit_array(elemtype, val_lit_strs):
34 (elem_type, len(val_lit_strs), _lit_aggregate_uncasted(val_lit_strs))
37 def _lit_addressof(v):
41 if isinstance(v, int):
42 return _lit_integer(v)
44 return v # should already be an integer
46 class DonorStructLayout():
47 def __init__(l, typename):
48 x = gdb.parse_and_eval('(%s){ }' % typename)
49 l._typename = typename
52 for f in x.type.fields():
53 l._posns[f.name] = len(l._template)
54 try: f.type.fields(); blank = '{ }'
55 except AttributeError: blank = '0'
56 l._template.append(blank)
57 def substitute(l, values):
58 build = copy.deepcopy(l._template)
59 for (k,v) in values.items():
60 build[ l._posns[k] ] = _make_lit(v)
61 return '((%s)%s)' % (l._typename, _lit_aggregate_uncasted(build))
63 class DonorImplementation():
66 di._saved_errno = None
67 di._result_stream = os.fdopen(3, 'w')
70 # sigh, we have to record the order of the arguments!
71 def _find_fields(typename):
73 fields = di._structs[typename]
74 except AttributeError:
75 fields = DonorStructLayout(typename)
76 di._structs[typename] = fields
79 def _make(typename, values):
80 fields = di._find_fields(typename)
81 return fields.substitute(values)
83 # calling functions (need to cast the function name to the right
84 # type in case maybe gdb doesn't know the type)
86 def _func(di, functype, funcname, realargs):
87 expr = '((%s) %s) %s' % (functype, funcname, realargs)
88 return gdb.parse_and_eval(expr)
90 def _must_func(di, functype, funcname, realargs):
91 retval = di._func(functype, funcname, realargs)
93 errnoval = gdb.parse_and_eval('errno')
94 raise RuntimeError("%s gave errno=%d `%s'" %
95 (funcname, errnoval, os.strerror(errnoval)))
97 # wrappers for the syscalls that do what we want
99 def _sendmsg(di, carrier, control_msg):
100 iov_base = _lit_array('int', map(str,fds))
101 iov = di._make('struct iovec', {
102 'iov_base': iov_base,
103 'iov_len' : len(fds),
106 msg = di._make('struct msghdr', {
107 'msg_iov' : _lit_addressof(iov),
109 'msg_control' : _lit_array('char', control_msg),
110 'msg_controllen': len(control_msg),
114 'ssize_t (*)(int, const struct msghdr*, int flags)',
116 '(%s, %s, 0)' % (carrier, _lit_addressof(msg))
120 return di._must_func(
121 'int (*)(int, int, int)',
123 '(%d, %d, 0)' % (socket.AF_UNIX, socket.SOCK_STREAM)
126 def _connect(di, fd, path):
127 addr = di._make('struct sockaddr_un', {
128 'sun_family' : _lit_integer(socket.AF_UNIX),
129 'sun_path' : _lit_string_uncasted(path),
133 'int (*)(int, const struct sockaddr*, socklen_t)',
135 '(%d, (const struct sockaddr*)%s, sizeof(struct sockaddr_un))'
136 % (fd, _lit_addressof(addr))
140 di._must_func('int (*)(int)', 'close', '(%d)' % fd)
142 def _mkdir(di, path, mode):
144 'int (*)(const char*, mode_t)',
146 '("%s", %d)' % (_string_escape_for_c(path), mode)
149 errnoval = gdb.parse_and_eval('errno')
150 if errnoval != os.errno.EEXIST:
151 raise RuntimeError("mkdir %s failed: `%s'" %
152 (repr(path), os.strerror(errnoval)))
157 di._saved_errno = gdb.parse_and_eval('errno')
159 def _errno_restore(di):
160 to_restore = di._saved_errno
161 di._saved_errno = None
162 if to_restore is not None:
163 gdb.parse_and_eval('errno = %d' % to_restore)
167 def result(di, output):
168 di._result_stram.write(output)
169 di._result_stram.flush()
171 def donate(di, path, control_msg):
172 # control_msg is an array of integers being the ancillary data
173 # array ("control") for sendmsg, and hence specifies which fds
179 carrier = di._socket()
180 di._connect(carrier, path)
181 di._sendmsg(carrier, control_msg)
185 if carrier is not None:
186 try: di._close(carrier)
187 except Exception: pass
195 val = di._must_func('uid_t (*)(void)', 'geteuid', '()')
199 di._result('%d\n' % val)
204 val = di._mkdir(path, '0700')
208 di._result('%d\n' % val)
210 def _protocol_read(di):
211 return sys.stdin.readline().rstrip('\n')
216 cmd = di._protocol_read()