chiark / gitweb /
fishdescriptor: work on python code
[chiark-utils.git] / fishdescriptor / greenend / fishdescriptor / indonor.py
1
2 # class for use inside gdb which is debugging the donor process
3
4 import gdb
5
6 def _string_escape_for_c(s):
7     if not isinstance(s, bytes):
8         s = s.encode('utf-8') # sigh, python 2/3 compat
9     out = ''
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:
12             out += '\\x%02x' % c
13         else:
14             out += chr(c)
15     return out
16
17 class DonorImplementation():
18     def __init__(self):
19         # works on the current gdb.Inferior
20         # ideally should be reused if the same process is targetd
21         self._open = None
22         self._sym = None
23
24     def _func(self, functype, funcname, realargs):
25         expr = '((%s) %s) %s' % (functype, funcname, realargs)
26         return gdb.parse_and_eval(expr)
27
28     def _dlfunc(self, functype, funcname, realargs):
29         r = self._func(functype,funcname,realargs)
30         if not r:
31             err = self._func('char* (*)(void)', 'dlerror', '()')
32             if not err:
33                 err = 'dlerror said NULL!'
34             else:
35                 err = err.string()
36             raise RuntimeError("%s failed: %s" % (funcname, err))
37         return r
38
39     def _dlopen(self):
40         if self._open is not None: return
41         rtld_print_cmd = ['fishdescriptor','--print-rtld-now'];
42         rtld_now = subprocess.check_output(rtld_print_cmd).rstrip('\n')
43         o = self._dlfunc('void* (*)(const char*, int)',
44                          'dlopen',
45                          '("libfishdescriptor-donate.so.1.0", %s)' % rtld_now)
46         self._open = '((void*)%s)' % o
47
48     def _dlsym(self):
49         if self._sym is not None: return
50         self._sym = self._dlfunc('void* (*)(void*, const char*)'
51                                  'dlsym',
52                                  '(%s, "fishdescriptor_donate")' % self._open)
53
54     def donate(self, path, fds):
55         self._dlopen()
56         self._dlsym()
57         r = self._func('int (*)(const char*, const int*)',
58                        self._sym,
59                        '("%s", (int[%d]){ %s, -1 })'
60                        % (_string_escape_for_c(path),
61                           len(fds) + 1,
62                           ', '.join(["%d" % fd for fd in fds])))
63         if r:
64             err = self._func('char* (*)(int)',
65                              strerror,
66                              r)
67             if not err:
68                 err = 'strerror failed'
69             else:
70                 err = err.string()
71             raise RuntimeError("donate failed: %s" % err)