Skip to content

detached child_process doesn't inherit stdio #3596

Description

@yepitschunked

The following snippet of code prints the child process' output if detached is true, and doesn't if it's false:

  cmd = '...'
  child_process.spawn(cmd, [], {
    stdio: 'inherit',
    detached: true
  });

This part of the docs seems relevant:

If the parent's stdio is inherited, the child will remain attached to the controlling terminal.

Which seems to imply that inheriting these streams is supported. Not sure if this is just me misreading the docs or...

Activity

added
child_processIssues and PRs related to the child_process subsystem.
on Oct 30, 2015

bnoordhuis commented on Oct 30, 2015

@bnoordhuis
Member

{ detached: true } is sugar for setsid(2), meaning it disconnects from the controlling tty. Does that answer you question? If not, can you post a complete example with expected and actual output?

cjihrig commented on Oct 30, 2015

@cjihrig
Contributor

@bnoordhuis is this expected behavior? With Node 5 on OS X, and the following code:

process.stdout;
require('child_process').spawn('ls', [], {
  stdio: 'inherit',
  detached: true
});

Nothing is printed. However, if I comment out the process.stdout on the first line, then ls output is displayed.

bnoordhuis commented on Oct 30, 2015

@bnoordhuis
Member

/cc @indutny - I'm guessing it's related to OS X's broken kqueue support for special files and libuv's workaround. I can reproduce on OS X but it works fine on Linux, FWIW.

indutny commented on Oct 31, 2015

@indutny
Member

The only explanation that I have is that reopening stdout as /dev/tty somehow interferes with it.

indutny commented on Dec 21, 2015

@indutny
Member

Gosh, it is twice as odd, considering that piping the output like this: node test.js | less works just fine in both cases. I suspect some OS X oddness atm...

indutny commented on Dec 21, 2015

@indutny
Member

And of course it is related to the reopening of /dev/tty in libuv: https://github.com/libuv/libuv/blob/25506bb33156f15e27a837979773264dfe8d06be/src/unix/tty.c#L64-L87 . Removing that branch fixes the problem.

indutny commented on Dec 22, 2015

@indutny
Member

I think there is some fishy stuff with the current session and the way /dev/tty attaches to it on OS X, but I am not yet quite sure what exactly is happening there.

indutny commented on Dec 22, 2015

@indutny
Member

It looks like for a child process write returns EIO:

write(0x1, "\0", 0x1)        = -1 Err#5

indutny commented on Dec 22, 2015

@indutny
Member

@wonnage may I ask you to give a try to this patch?

diff --git a/deps/uv/src/unix/process.c b/deps/uv/src/unix/process.c
index 571f8cd..c02c750 100644
--- a/deps/uv/src/unix/process.c
+++ b/deps/uv/src/unix/process.c
@@ -283,8 +283,10 @@ static void uv__process_child_init(const uv_process_options_t* options,
   int use_fd;
   int fd;

-  if (options->flags & UV_PROCESS_DETACHED)
+  if (options->flags & UV_PROCESS_DETACHED) {
+    setpgid(getpid(), getpid());
     setsid();
+  }

   /* First duplicate low numbered fds, since it's not safe to duplicate them,
    * they could get replaced. Example: swapping stdout and stderr; without

I suspect that this is an OS X kernel bug and that for some reason pg->pg_jobc is 0 if setsid() is called alone.

indutny commented on Dec 23, 2015

@indutny
Member

Gosh, I have figured it out. The actual device behind /dev/tty lives in bsd/kern/tty_tty.c. The thing about this device is that it always looks up the tty using current proc's session. So when you create a new session, it doesn't have any tty assigned to it, and all syscalls on it will result in EIO error.

I'll need some time to think if there is any workaround for this.

indutny commented on Dec 23, 2015

@indutny
Member

I failed at it miserably :( Submitted Apple bugreport # 23994737

yepitschunked commented on Jan 21, 2016

@yepitschunked
Author

Hey! sorry i've been inactive on this thread - switched jobs and no longer working on the code that produced this error in the first place :)

yepitschunked commented on Jan 21, 2016

@yepitschunked
Author

handwaving through my ignorance of unix internals a bit, but is this a correct summary of the issue:

we start with a parent process hooked up to a tty
setsid makes the new process lose controlling terminal (/dev/tty isn't backed up by anything)

afterwards, piping node test.js | less works because stdout is going to stdin of less; less is in the same session as parent and writes to controlling tty just fine, and we get output.

however, if stdout is a tty (node test.js), the uv change you linked attempts to reopen /dev/tty, but at this point /dev/tty doesn't point anywhere in the new process (due to OSX). is there some way to save a reference to correct tty and pass that to the child process in uv_tty_init when calling uv__open_cloexec?

indutny commented on Jan 22, 2016

@indutny
Member

@wonnage I believe piping works because it is not using tty at all, it is using pipes.

added
macosIssues and PRs related to the macOS platform.
on Mar 4, 2016

aoberoi commented on Mar 25, 2016

@aoberoi
Contributor

@indutny would you mind sharing the apple bug report on open radar?

indutny commented on Mar 25, 2016

@indutny
Member

jasnell commented on Jun 7, 2016

@jasnell
Member

Curious if this is fixed with libuv 1.9.x

jasnell commented on Jun 7, 2016

@jasnell
Member

Answering my own question... running @cjihrig's test case on v6+ works just fine.

Closing as resolved.

craigsdennis commented on Oct 1, 2016

@craigsdennis

Seeing something very similar with stdin on Windows and Node v6.7.1 anyone know the root of this and if there is another bug not linked here that might contain what the fix was?

cc/ @chalkers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    child_processIssues and PRs related to the child_process subsystem.macosIssues and PRs related to the macOS platform.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions