@@ -53,6 +53,17 @@ wrap_executable () {
53
53
sed -i -E " s|^EXECUTABLE=.*|EXECUTABLE='${executable##*/ } '|" " $out "
54
54
}
55
55
56
+ add_bin_shell () {
57
+ cat > " $1 " << EOF
58
+ #!/bin/env ${1%/* } /python
59
+ import shutil
60
+ import subprocess
61
+ import sys
62
+ if __name__ == '__main__':
63
+ subprocess.run([shutil.which('/bin/bash') or '/bin/sh', *sys.argv[1:]])
64
+ EOF
65
+ }
66
+
56
67
wrap_all () (
57
68
for file in " $bin " /* ; do
58
69
# shellcheck disable=SC2015
@@ -74,6 +85,11 @@ wrap_all () (
74
85
chmod +x " $file "
75
86
echo " $file "
76
87
done
88
+
89
+ file=" $( realpath " $bin /shell" ) "
90
+ add_bin_shell " $file "
91
+ chmod +x " $file "
92
+ echo " $file "
77
93
)
78
94
79
95
wrap_all " $@ "
@@ -104,6 +120,7 @@ pip_return_status=$?
104
120
new_binaries=" $(
105
121
for file in " $venv /bin" /* ; do
106
122
[ -L " $file " ] || [ ! -x " $file " ] ||
123
+ [ " ${file##*/ } " = ' shell' ] ||
107
124
is_already_wrapped " $file " ||
108
125
is_python_shebang " $file " ||
109
126
printf ' %s' " ${file##*/ } "
@@ -133,47 +150,66 @@ warn () { echo "sandbox-venv/wrapper: $*" >&2; }
133
150
134
151
venv=" $( realpath " ${0%/* } /.." ) "
135
152
136
- EXECUTABLE=" ${1:-/ usr/ bin/ python} "
153
+ # Quote args with spaces
154
+ format_args () {
155
+ for arg in " $@ " ; do case " $arg " in
156
+ $venv /*) printf " %s " " ${venv##*/ } /${arg# " $venv /" } " ;;
157
+ *\ *) printf " ' %s' " " $arg " ;;
158
+ *) printf " %s " " $arg " ;;
159
+ esac; done
160
+ }
161
+ formatted_cmdline=" python $( format_args " $@ " ) "
162
+
163
+ EXECUTABLE=" ${1:-/ usr/ bin/ python3} "
137
164
_BWRAP_DEFAULT_ARGS=
138
165
139
- [ -e " $venv /bin/python " ] # Assertion
166
+ [ -e " $venv /bin/python3 " ] # Assertion
140
167
141
168
# Expose these binaries
142
169
executables="
143
- /usr/bin/python
170
+ /usr/bin/python3
171
+
172
+ /usr/bin/git
173
+
174
+ /bin/bash
144
175
/bin/env
145
176
/bin/ls
146
- /bin/bash
147
177
/bin/sh"
148
178
149
179
case $- in *x*) xtrace=-x ;; *) xtrace=+x ;; esac; set +x
150
180
151
181
# Collect binaries' lib dependencies
152
182
lib_deps () {
153
183
readelf -l " $1 " | awk '/interpreter/ {print $NF }' | tr -d '[]'
154
- ldd " $1 " | awk '/=>/ { print $3 }' | grep -E '^/'
184
+ ldd " $1 " | awk '/=>/ { print $3 }' | { grep -E '^/' || true; }
155
185
}
156
186
collect=" $executables "
157
187
for exe in $executables ; do
158
188
collect=" $collect
159
189
$( lib_deps " $exe " ) "
160
190
done
161
191
162
- # Explicit Python dependencies from Firejail
163
- # TODO: Get lib deps from venv/lib/*.so
192
+ # Collect lib deps from venv/lib/*.so
193
+ root_so_lib_dirs="
194
+ /usr/lib/python3* /lib-dynload
195
+ /usr/lib64/python3* /lib-dynload"
196
+ for exe in $( find " $venv /lib" $root_so_lib_dirs -name ' *.so' 2> /dev/null || true) ; do
197
+ collect=" $collect
198
+ $( lib_deps " $exe " ) "
199
+ done
200
+
201
+ # Explicit Python deps we know of
164
202
py_libs="
165
203
/usr/include/python3*
166
204
/usr/lib/python3*
167
205
/usr/lib64/python3*
168
- /usr/local/lib/python3*
169
- /usr/share/python3*
170
- /usr/lib/* /libreadline.so*
171
- /usr/lib/** /libreadline.so*
172
- /usr/lib/** /libssl.so*
173
- /usr/lib/** /libcrypto.so*
174
- "
206
+ /usr/local/lib/python3* "
207
+ git_libs="
208
+ /usr/lib* /git-core
209
+ "
175
210
ro_bind_extra="
176
211
/etc/resolv.conf
212
+ /usr/share/zoneinfo
177
213
/usr/share/ca-certificates*
178
214
/etc/pki
179
215
/etc/ssl
@@ -183,6 +219,7 @@ ro_bind_extra="
183
219
collect="
184
220
$collect
185
221
$ro_bind_extra
222
+ $git_libs
186
223
$py_libs "
187
224
188
225
# Filter collect, warn on non-existant paths, unique sort, cull.
@@ -232,11 +269,13 @@ for path in $ro_bind_pwd_extra; do
232
269
done
233
270
set -- --bind " $proj_dir " " $proj_dir " " $@ "
234
271
235
- # RW bind cache dir for downloads etc.
272
+ # RW bind cache dirs for downloads etc.
236
273
home=" ${HOME:- " /home/$USER " } "
237
- pip_cache=" $home /.cache/pip"
274
+ pip_cache=" $home /${XDG_CACHE_HOME :- .cache} /pip"
238
275
mkdir -p " $venv /cache" " $pip_cache "
239
- # Use .venv/cache for general cache, $HOME /.cache/pip for pip cache
276
+ mkdir -p " $venv /cache/pip" &&
277
+ echo " This is an artefact of Bubblewrap bind mounting. Real pip cache is in \$ HOME/.cache/pip" \
278
+ > " $venv /cache/pip/note.txt"
240
279
set -- --bind " $venv /cache" " $home /.cache" \
241
280
--bind " $pip_cache " " $home /.cache/pip" " $@ "
242
281
@@ -247,24 +286,27 @@ done
247
286
248
287
set $xtrace
249
288
250
- # Quote args with spaces
251
- format_args () ( set +x; for arg in " $@ " ; do case " $arg " in *\ *) printf " ' %s' " " $arg " ;; *) printf " %s " " $arg " ;; esac; done; )
252
- warn " exec bwrap ${0%/* } /$EXECUTABLE $( format_args " $@ " ) "
289
+ warn " exec bwrap [...] $formatted_cmdline "
290
+
291
+ uid=" $( id -u) "
292
+ cwd=" $( pwd) "
293
+
294
+ [ ! " ${VERBOSE:- ${verbose:- } } " ] || set -x
253
295
254
296
# shellcheck disable=SC2086
255
297
exec bwrap \
256
298
--dir /tmp \
257
- --dir " /run/user/$( id -u ) " \
258
- --dir " $( pwd ) " \
259
- --chdir " $( pwd ) " \
299
+ --dir " /run/user/$uid " \
300
+ --dir " $cwd " \
301
+ --chdir " $cwd " \
260
302
--proc /proc \
261
303
--dev /dev \
262
304
--clearenv \
263
305
--unshare-all \
264
306
--share-net \
265
307
--new-session \
266
308
--die-with-parent \
267
- --setenv PS1 '\u @ \h \$ ' \
309
+ --setenv PS1 '\u @ \h \$ ' \
268
310
--setenv HOME " $home " \
269
311
--setenv USER " user" \
270
312
--setenv VIRTUAL_ENV " $venv " \
0 commit comments