Skip to content

Instantly share code, notes, and snippets.

@adamhooper
Last active November 27, 2019 19:24
Show Gist options
  • Save adamhooper/441e3905c7a28193300f73ac5d7de20e to your computer and use it in GitHub Desktop.
Save adamhooper/441e3905c7a28193300f73ac5d7de20e to your computer and use it in GitHub Desktop.
Clone a process with new file descriptors
import os
import socket
from c_clone import libc_clone
# Primer on global variables: we set these before clone(), so they're set in
# both the spawner process and the child process. Set them to None when they're
# no longer needed, to make code easier to read.
#
# Primer on pipes: `os.pipe()` creates two file descriptors: a "read" and a
# "write". Data written to the "write" end can (and must) be read by the "read"
# pipe. The "read" end will only reach EOF after the "write" end is closed.
#
# Primer on file descriptors ("fd"s): an fd is an integer. The kernel lets you
# use this fd in system calls. You can duplicate ("dup") an fd to give it a new
# integer; then both fds will behave as one. A clone()d process starts with dups
# of all the parent's fds. Close _all_ dups of an fd (across all processes) to
# close the underlying resource.
stdin_r, stdin_w = None, None
stdout_r, stdout_w = None, None
stderr_r, stderr_w = None, None
# Primer on sockets: we use it like a pipe, but it can send file descriptors
sock = None
def _child_close_socket():
global sock
os.close(sock.fileno())
sock = None
def _child_reset_fds():
global stdin_r, stdin_w, stdout_r, stdout_w, stderr_r, stderr_w
os.close(stdin_w)
os.close(stdout_r)
os.close(stderr_r)
if stdin_r > 0:
os.dup2(stdin_r, 0)
os.close(stdin_r)
if stdout_w > 1:
os.dup2(stdout_w, 1)
os.close(stdout_w)
if stderr_w > 2:
os.dup2(stderr_w, 2)
os.close(stderr_w)
stdin_r, stdin_w = None, None
stdout_r, stdout_w = None, None
stderr_r, stderr_w = None, None
def _spawner_create_fds_before_clone():
global stdin_r, stdin_w, stdout_r, stdout_w, stderr_r, stderr_w
stdin_r, stdin_w = os.pipe()
stdout_r, stdout_w = os.pipe()
stderr_r, stderr_w = os.pipe()
def _spawner_clean_fds_after_clone():
global stdin_r, stdin_w, stdout_r, stdout_w, stderr_r, stderr_w
stdin_r, stdin_w = None, None
stdout_r, stdout_w = None, None
stderr_r, stderr_w = None, None
def _spawner_send_child_info_to_parent(child_pid):
# ... send parent (child_pid, stdin_w, stdout_r, stderr_r)
pass
def child():
_child_close_socket()
_child_reset_fds()
print("Hello from the child!")
os._exit(0)
def spawner():
# ... import Python modules
while True:
# Wait for parent to signal us
_spawner_create_fds_before_clone()
child_pid = libc_clone(child)
_spawner_send_child_info_to_parent(child_pid)
_spawner_clean_fds_after_clone()
# ... parent must read() from stdout_r and stderr_r. (We've closed
# all other duplicates of those fds.)
#
# ... parent _must_ call os.waitpid(child_pid, os.WEXITED).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment