-
Notifications
You must be signed in to change notification settings - Fork 4
/
shared_python.py
160 lines (119 loc) · 4.76 KB
/
shared_python.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
""" Shared snippets between the various python scripts of this project """
import subprocess
import re
import os
from collections import namedtuple
DEFAULT_AUX_DIRS = [
'~/.cache/eh_elfs',
]
def to_eh_elf_path(so_path, out_dir, base=False):
''' Transform a library path into its eh_elf counterpart '''
base_path = os.path.basename(so_path) + '.eh_elf'
if base:
return base_path
return os.path.join(out_dir, base_path + '.so')
def find_eh_elf_dir(obj_path, aux_dirs, out_dir):
''' Find the directory in which the eh_elf corresponding to `obj_path` will
be outputted, among the output directory and the aux directories '''
for candidate in aux_dirs:
eh_elf_path = to_eh_elf_path(obj_path, candidate)
if os.path.exists(eh_elf_path):
return candidate
# No match among the aux dirs
return out_dir
def readlink_rec(path):
''' Returns the canonical path of `path`, resolving multiple layers of
symlinks '''
while os.path.islink(path):
path = os.path.join(
os.path.dirname(path),
os.readlink(path))
return path
def is_newer(file1, file2):
''' Returns True iff file1 is newer than file2 '''
try:
f1_mtime = os.path.getmtime(file1)
f2_mtime = os.path.getmtime(file2)
if f1_mtime > f2_mtime:
return True
except OSError:
pass
return False
def elf_so_deps(path):
''' Get the list of shared objects dependencies of the given ELF object.
This is obtained by running `ldd`. '''
deps_list = []
try:
ldd_output = subprocess.check_output(['/usr/bin/ldd', path]) \
.decode('utf-8')
ldd_re = re.compile(r'^.* => (.*) \(0x[0-9a-fA-F]*\)$')
ldd_lines = ldd_output.strip().split('\n')
for line in ldd_lines:
line = line.strip()
match = ldd_re.match(line)
if match is None:
continue # Just ignore that line — it might be eg. linux-vdso
deps_list.append(match.group(1))
return deps_list
except subprocess.CalledProcessError as exn:
raise Exception(
("Cannot get dependencies for {}: ldd terminated with exit code "
"{}.").format(path, exn.returncode))
def do_remote(remote, command, send_files=None, retr_files=None):
''' Execute remotely (via ssh) a given command
The command is executed on the machine described by `remote` (see ssh(1)).
If `preload` is set, then the remote file at this path will be sourced
before running any command, allowing to set PATH and other variables.
send_files is a list of file paths that must be first copied at the root of
a temporary directory on `remote` before running the command. Consider
yourself jailed in that directory.
retr_files is a list of files that will be copied to the local machine
after the command is executed. Each list item can either be a string, which
is both the path on the remote and the local machine; or a pair
`(file_name, local_path)`. In the latter case, `file_name` is copied as
`local_path/file_name` if `local_path` is a directory, or as `local_path`
otherwise, on the local machine.
'''
if send_files is None:
send_files = []
if retr_files is None:
retr_files = []
def ssh_do(cmd_args, working_directory=None):
try:
cmd = ['ssh', remote]
if working_directory:
cmd += ['cd', working_directory, '&&']
cmd += cmd_args
return subprocess.check_output(cmd).decode('utf-8').strip()
except subprocess.CalledProcessError as e:
return None
def ssh_copy(what, where, is_upload):
if is_upload:
where = '{}:{}'.format(remote, where)
else:
what = '{}:{}'.format(remote, what)
subprocess.check_output(['scp', what, where])
TransferredFile = namedtuple('TransferredFile', 'local remote')
def interpret_transferred_file(descr):
if isinstance(descr, type('')):
return TransferredFile(descr, descr)
if os.path.isdir(descr[1]):
to = os.path.join(descr[1], descr[0])
else:
to = descr[1]
return TransferredFile(descr[0], to)
# Create temp dir
tmp_dir = ssh_do(['mktemp', '-d'])
# Upload `send_files`
for f in send_files:
dest = tmp_dir+'/'
ssh_copy(f, dest, is_upload=True)
# Do whatever must be done
output = ssh_do(command, working_directory=tmp_dir)
# Download `retr_files`
for f in map(interpret_transferred_file, retr_files):
src = os.path.join(tmp_dir, f.local)
ssh_copy(src, f.remote, is_upload=False)
# Remove temp dir
ssh_do(['rm', '-rf', tmp_dir])
return output