|
45 | 45 | write_ = libc.func("i", "write", "iPi")
|
46 | 46 | close_ = libc.func("i", "close", "i")
|
47 | 47 | dup_ = libc.func("i", "dup", "i")
|
| 48 | + dup2_ = libc.func("i", "dup2", "ii") |
48 | 49 | access_ = libc.func("i", "access", "si")
|
49 | 50 | fork_ = libc.func("i", "fork", "")
|
50 | 51 | pipe_ = libc.func("i", "pipe", "p")
|
@@ -207,6 +208,10 @@ def dup(fd):
|
207 | 208 | check_error(r)
|
208 | 209 | return r
|
209 | 210 |
|
| 211 | +def dup2(oldfd, newfd): |
| 212 | + r = dup2_(oldfd, newfd) |
| 213 | + check_error(r) |
| 214 | + return r |
210 | 215 |
|
211 | 216 | def access(path, mode):
|
212 | 217 | return access_(path, mode) == 0
|
@@ -293,24 +298,53 @@ def urandom(n):
|
293 | 298 | with builtins.open("/dev/urandom", "rb") as f:
|
294 | 299 | return f.read(n)
|
295 | 300 |
|
| 301 | +class _PopenStream: |
| 302 | + def __init__(self, fd, pid, mode): |
| 303 | + self._fd = open(f"/dev/fd/{fd}", mode) |
| 304 | + self._pid = pid |
| 305 | + self._closed = False |
| 306 | + self._exitcode = None |
296 | 307 |
|
297 |
| -def popen(cmd, mode="r"): |
298 |
| - import builtins |
| 308 | + def __enter__(self): |
| 309 | + return self._fd |
| 310 | + |
| 311 | + def __exit__(self, exc_type, exc_val, exc_tb): |
| 312 | + self.close() |
299 | 313 |
|
300 |
| - i, o = pipe() |
301 |
| - if mode[0] == "w": |
302 |
| - i, o = o, i |
| 314 | + def close(self): |
| 315 | + if not self._closed: |
| 316 | + try: |
| 317 | + self._fd.close() |
| 318 | + finally: |
| 319 | + pid, status = waitpid(self._pid, 0) |
| 320 | + self._exitcode = status |
| 321 | + self._closed = True |
| 322 | + return self._exitcode |
| 323 | + |
| 324 | + |
| 325 | +def popen(cmd, mode='r'): |
| 326 | + rfd, wfd = pipe() |
303 | 327 | pid = fork()
|
304 |
| - if not pid: |
305 |
| - if mode[0] == "r": |
306 |
| - close(1) |
| 328 | + |
| 329 | + if pid == 0: |
| 330 | + # --- CHILD --- |
| 331 | + if mode[0] == 'r': |
| 332 | + close(rfd) |
| 333 | + dup2(wfd, 1) # connect pipe to stdout |
| 334 | + close(wfd) |
307 | 335 | else:
|
308 |
| - close(0) |
309 |
| - close(i) |
310 |
| - dup(o) |
311 |
| - close(o) |
312 |
| - s = system(cmd) |
313 |
| - _exit(s) |
| 336 | + close(wfd) |
| 337 | + dup2(rfd, 0) # connect pipe to stdin |
| 338 | + close(rfd) |
| 339 | + |
| 340 | + execvp("sh", ["sh", "-c", cmd]) |
| 341 | + # If execvp SUCCEEDS, the code below is NEVER executed. |
| 342 | + _exit(127) |
| 343 | + |
| 344 | + # --- PARENT --- |
| 345 | + if mode[0] == 'r': |
| 346 | + close(wfd) |
| 347 | + return _PopenStream(rfd, pid, mode) |
314 | 348 | else:
|
315 |
| - close(o) |
316 |
| - return builtins.open(i, mode) |
| 349 | + close(rfd) |
| 350 | + return _PopenStream(wfd, pid, mode) |
0 commit comments