'cmd is the full string that will be exec\'d.  it includes the program
name and all its arguments
stdin, stdout, stderr are what the child will use for standard
input/output/err
call_args is a mapping of all the special keyword arguments to apply
to the child process'

def __init__(self, command, parent_log, cmd, stdin, stdout, stderr, call_args, pipe, process_assign_lock):

    self.command = command
    self.call_args = call_args
    ca = self.call_args
    if (ca['uid'] is not None):
        if (os.getuid() != 0):
            raise RuntimeError('UID    setting    requires    root    privileges')
        target_uid = ca['uid']
        pwrec = pwd.getpwuid(ca['uid'])
        target_gid = pwrec.pw_gid
    if ca['piped']:
        ca['tty_out'] = False
    self._stdin_process = None
    stdin_is_tty_or_pipe = (ob_is_tty(stdin) or ob_is_pipe(stdin))
    stdout_is_tty_or_pipe = (ob_is_tty(stdout) or ob_is_pipe(stdout))
    stderr_is_tty_or_pipe = (ob_is_tty(stderr) or ob_is_pipe(stderr))
    tee_out = (ca['tee'] in (True, 'out'))
    tee_err = (ca['tee'] == 'err')
    custom_in_out_err = (stdin or stdout or stderr)
    single_tty = ((ca['tty_in'] and ca['tty_out']) and (not custom_in_out_err))
    if single_tty:
        (self._stdin_read_fd, self._stdin_write_fd) = pty.openpty()
        self._stdout_read_fd = os.dup(self._stdin_read_fd)
        self._stdout_write_fd = os.dup(self._stdin_write_fd)
        self._stderr_read_fd = os.dup(self._stdin_read_fd)
        self._stderr_write_fd = os.dup(self._stdin_write_fd)
    else:
        if (isinstance(stdin, OProc) and stdin.call_args['piped']):
            self._stdin_write_fd = stdin._pipe_fd
            self._stdin_read_fd = None
            self._stdin_process = stdin
        elif stdin_is_tty_or_pipe:
            self._stdin_write_fd = os.dup(get_fileno(stdin))
            self._stdin_read_fd = None
        elif ca['tty_in']:
            (self._stdin_read_fd, self._stdin_write_fd) = pty.openpty()
        else:
            (self._stdin_write_fd, self._stdin_read_fd) = os.pipe()
        if (stdout_is_tty_or_pipe and (not tee_out)):
            self._stdout_write_fd = os.dup(get_fileno(stdout))
            self._stdout_read_fd = None
        elif ca['tty_out']:
            (self._stdout_read_fd, self._stdout_write_fd) = pty.openpty()
        else:
            (self._stdout_read_fd, self._stdout_write_fd) = os.pipe()
        if (stderr is OProc.STDOUT):
            if (stdout_is_tty_or_pipe and (not tee_out)):
                self._stderr_read_fd = None
            else:
                self._stderr_read_fd = os.dup(self._stdout_read_fd)
            self._stderr_write_fd = os.dup(self._stdout_write_fd)
        elif (stderr_is_tty_or_pipe and (not tee_err)):
            self._stderr_write_fd = os.dup(get_fileno(stderr))
            self._stderr_read_fd = None
        else:
            (self._stderr_read_fd, self._stderr_write_fd) = os.pipe()
    piped = ca['piped']
    self._pipe_fd = None
    if piped:
        fd_to_use = self._stdout_read_fd
        if (piped == 'err'):
            fd_to_use = self._stderr_read_fd
        self._pipe_fd = os.dup(fd_to_use)
    new_session = ca['new_session']
    needs_ctty = (ca['tty_in'] and new_session)
    self.ctty = None
    if needs_ctty:
        self.ctty = os.ttyname(self._stdin_write_fd)
    cwd = ca['cwd']
    if ((cwd is not None) and (not os.path.exists(cwd))):
        os.chdir(cwd)
    gc_enabled = gc.isenabled()
    if gc_enabled:
        gc.disable()
    (session_pipe_read, session_pipe_write) = os.pipe()
    (exc_pipe_read, exc_pipe_write) = os.pipe()
    if IS_OSX:
        (close_pipe_read, close_pipe_write) = os.pipe()
    self.sid = None
    self.pgid = None
    self.pid = os.fork()
    if (self.pid == 0):
        if IS_OSX:
            os.read(close_pipe_read, 1)
            os.close(close_pipe_read)
            os.close(close_pipe_write)
        try:
            if (ca['bg'] is True):
                signal.signal(signal.SIGHUP, signal.SIG_IGN)
            signal.signal(signal.SIGPIPE, signal.SIG_DFL)
            if new_session:
                os.setsid()
            else:
                os.setpgrp()
            sid = os.getsid(0)
            pgid = os.getpgid(0)
            payload = ('%d,%d' % (sid, pgid)).encode(DEFAULT_ENCODING)
            os.write(session_pipe_write, payload)
            if (ca['tty_out'] and (not stdout_is_tty_or_pipe) and (not single_tty)):
                tty.setraw(self._stdout_write_fd)
            if self._stdin_read_fd:
                os.close(self._stdin_read_fd)
            if self._stdout_read_fd:
                os.close(self._stdout_read_fd)
            if self._stderr_read_fd:
                os.close(self._stderr_read_fd)
            os.close(session_pipe_read)
            os.close(exc_pipe_read)
            if cwd:
                os.chdir(cwd)
            os.dup2(self._stdin_write_fd, 0)
            os.dup2(self._stdout_write_fd, 1)
            os.dup2(self._stderr_write_fd, 2)
            if needs_ctty:
                tmp_fd = os.open(os.ttyname(0), os.O_RDWR)
                os.close(tmp_fd)
            if (ca['tty_out'] and (not stdout_is_tty_or_pipe)):
                setwinsize(1, ca['tty_size'])
            if (ca['uid'] is not None):
                os.setgid(target_gid)
                os.setuid(target_uid)
            preexec_fn = ca['preexec_fn']
            if callable(preexec_fn):
                preexec_fn()
            max_fd = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
            os.closerange(3, max_fd)
            if (ca['env'] is None):
                os.execv(cmd[0], cmd)
            else:
                os.execve(cmd[0], cmd, ca['env'])
        except:
            try:
                tb = traceback.format_exc().encode('utf8', 'ignore')
                os.write(exc_pipe_write, tb)
            finally:
                os._exit(255)
    else:
        if gc_enabled:
            gc.enable()
        os.close(self._stdin_write_fd)
        os.close(self._stdout_write_fd)
        os.close(self._stderr_write_fd)
        if IS_OSX:
            os.close(close_pipe_read)
            os.write(close_pipe_write, str(1).encode(DEFAULT_ENCODING))
            os.close(close_pipe_write)
        os.close(exc_pipe_write)
        fork_exc = os.read(exc_pipe_read, (1024 ** 2))
        os.close(exc_pipe_read)
        if fork_exc:
            fork_exc = fork_exc.decode(DEFAULT_ENCODING)
            raise ForkException(fork_exc)
        os.close(session_pipe_write)
        (sid, pgid) = os.read(session_pipe_read, 1024).decode(DEFAULT_ENCODING).split(',')
        os.close(session_pipe_read)
        self.sid = int(sid)
        self.pgid = int(pgid)
        self.timed_out = False
        self.started = time.time()
        self.cmd = cmd
        self.exit_code = None
        self.stdin = (stdin or Queue())
        self._pipe_queue = Queue()
        self._wait_lock = threading.Lock()
        self._stdout = deque(maxlen=ca['internal_bufsize'])
        self._stderr = deque(maxlen=ca['internal_bufsize'])
        if (ca['tty_in'] and (not stdin_is_tty_or_pipe)):
            setwinsize(self._stdin_read_fd, ca['tty_size'])
        self.log = parent_log.get_child('process', repr(self))
        self.log.debug('started    process')
        if (ca['tty_in'] and (not stdin_is_tty_or_pipe)):
            attr = termios.tcgetattr(self._stdin_read_fd)
            attr[3] &= (~ termios.ECHO)
            termios.tcsetattr(self._stdin_read_fd, termios.TCSANOW, attr)
        potentially_has_input = (callable(stdout) or stdin)
        self._stdin_stream = None
        if (self._stdin_read_fd and potentially_has_input):
            log = self.log.get_child('streamwriter', 'stdin')
            self._stdin_stream = StreamWriter(log, self._stdin_read_fd, self.stdin, ca['in_bufsize'], ca['encoding'], ca['tty_in'])
        stdout_pipe = None
        if ((pipe is OProc.STDOUT) and (not ca['no_pipe'])):
            stdout_pipe = self._pipe_queue
        save_stdout = ((not ca['no_out']) and (tee_out or (stdout is None)))
        pipe_out = (ca['piped'] in ('out', True))
        pipe_err = (ca['piped'] in ('err',))
        self._stdout_stream = None
        if ((not pipe_out) and self._stdout_read_fd):
            if callable(stdout):
                stdout = construct_streamreader_callback(self, stdout)
            self._stdout_stream = StreamReader(self.log.get_child('streamreader', 'stdout'), self._stdout_read_fd, stdout, self._stdout, ca['out_bufsize'], ca['encoding'], ca['decode_errors'], stdout_pipe, save_data=save_stdout)
        elif self._stdout_read_fd:
            os.close(self._stdout_read_fd)
        self._stderr_stream = None
        if ((stderr is not OProc.STDOUT) and (not single_tty) and (not pipe_err) and self._stderr_read_fd):
            stderr_pipe = None
            if ((pipe is OProc.STDERR) and (not ca['no_pipe'])):
                stderr_pipe = self._pipe_queue
            save_stderr = ((not ca['no_err']) and ((ca['tee'] in ('err',)) or (stderr is None)))
            if callable(stderr):
                stderr = construct_streamreader_callback(self, stderr)
            self._stderr_stream = StreamReader(Logger('streamreader'), self._stderr_read_fd, stderr, self._stderr, ca['err_bufsize'], ca['encoding'], ca['decode_errors'], stderr_pipe, save_data=save_stderr)
        elif self._stderr_read_fd:
            os.close(self._stderr_read_fd)
        def timeout_fn():
            self.timed_out = True
            self.signal(ca['timeout_signal'])
        self._timeout_event = None
        self._timeout_timer = None
        if ca['timeout']:
            self._timeout_event = threading.Event()
            self._timeout_timer = threading.Timer(ca['timeout'], self._timeout_event.set)
            self._timeout_timer.start()
        handle_exit_code = None
        if ((not self.command._spawned_and_waited) and ca['bg_exc']):
            def fn(exit_code):
                with process_assign_lock:
                    return self.command.handle_command_exit_code(exit_code)
            handle_exit_code = fn
        self._quit_threads = threading.Event()
        thread_name = ('background    thread    for    pid    %d' % self.pid)
        self._bg_thread_exc_queue = Queue(1)
        self._background_thread = _start_daemon_thread(background_thread, thread_name, self._bg_thread_exc_queue, timeout_fn, self._timeout_event, handle_exit_code, self.is_alive, self._quit_threads)
        self._input_thread = None
        self._input_thread_exc_queue = Queue(1)
        if self._stdin_stream:
            close_before_term = (not needs_ctty)
            thread_name = ('STDIN    thread    for    pid    %d' % self.pid)
            self._input_thread = _start_daemon_thread(input_thread, thread_name, self._input_thread_exc_queue, self.log, self._stdin_stream, self.is_alive, self._quit_threads, close_before_term)
        self._stop_output_event = threading.Event()
        self._output_thread_exc_queue = Queue(1)
        thread_name = ('STDOUT/ERR    thread    for    pid    %d' % self.pid)
        self._output_thread = _start_daemon_thread(output_thread, thread_name, self._output_thread_exc_queue, self.log, self._stdout_stream, self._stderr_stream, self._timeout_event, self.is_alive, self._quit_threads, self._stop_output_event)
