Skip to content

Instantly share code, notes, and snippets.

@isaacs
Created April 3, 2012 17:43
Show Gist options
  • Select an option

  • Save isaacs/2294039 to your computer and use it in GitHub Desktop.

Select an option

Save isaacs/2294039 to your computer and use it in GitHub Desktop.

Revisions

  1. isaacs revised this gist Apr 6, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion stdio-2.js
    Original file line number Diff line number Diff line change
    @@ -24,7 +24,7 @@ index corresponds to a fd in the child. The value is one of the following:
    Setting this option enables the ChildProcess.send() method. If the
    child writes JSON messages to this file descriptor, then this will trigger
    ChildProcess.on('message'). If the child is a Node.js program, then
    the presents of an IPC channel will enable process.send() and
    the presence of an IPC channel will enable process.send() and
    process.on('message')
    5. positive integer - call tty_wrap.guessHandleType. Create the appropriate
    pipe_wrap type of thing for this. Note that this will generally not
  2. isaacs revised this gist Apr 6, 2012. No changes.
  3. isaacs revised this gist Apr 6, 2012. 1 changed file with 17 additions and 1 deletion.
    18 changes: 17 additions & 1 deletion stdio-2.js
    Original file line number Diff line number Diff line change
    @@ -70,6 +70,7 @@ child_process.js:
    * `ipc` {Boolean} Default=false
    * `readable` {Boolean} Default=true
    * `writable` {Boolean} Default=true
    * return: Pipe stream object
    By default, all created pipes are duplex. The readable/writable aspect is
    only set at the JavaScript layer to help prevent doing the wrong sort of
    @@ -78,7 +79,22 @@ stream.pipe(). At the low-level, there is no special half-duplex behavior.
    IPC is only enabled if explicitly requested, usually by setting 'ipc' as one
    of the stdio FD options.
    ## Exposure on the ChildProcess object.b
    The return object is a Stream object. It has a "remote" member which
    is an opaque object representing the other end of the pipe pair, and
    is actually used by spawn.
    Example:
    ```javascript
    var p = createPipe()
    var c = spawn(cmd, args, { stdio: [ p ] })
    p === c.stdin
    p.on('data', function (chunk) {
    console.error('c.stdin emitted data', chunk)
    })
    ```
    ## Exposure on the ChildProcess object.
    The stdin/stdout/stderr members will be set to a Stream object if set to
    `pipe`, or null otherwise.
  4. isaacs revised this gist Apr 5, 2012. 1 changed file with 14 additions and 1 deletion.
    15 changes: 14 additions & 1 deletion stdio-2.js
    Original file line number Diff line number Diff line change
    @@ -64,7 +64,7 @@ child_process.js:
    * createPipe() should be blessed as a public API so that users can create
    arbitrary duplex streams for stdio.
    function createPipe([options])
    ## createPipe([options])
    * `options` {Object}
    * `ipc` {Boolean} Default=false
    @@ -78,6 +78,19 @@ stream.pipe(). At the low-level, there is no special half-duplex behavior.
    IPC is only enabled if explicitly requested, usually by setting 'ipc' as one
    of the stdio FD options.
    ## Exposure on the ChildProcess object.b
    The stdin/stdout/stderr members will be set to a Stream object if set to
    `pipe`, or null otherwise.
    If an `ipc` channel was requested, then the ChildProcess object will have
    a `send(msg)` method, and will perhaps `emit('message', obj)` if the child
    writes JSON to that channel.
    Other stdio fds will be exposed on the `stdio` member. ChildProcess.stdio
    is an Array corresponding to the file descriptors opened in the child.
    child.stdio[0] === child.stdin, child.stdio[2] === child.stderr, etc.
    */

    // Examples
  5. isaacs revised this gist Apr 5, 2012. 1 changed file with 49 additions and 17 deletions.
    66 changes: 49 additions & 17 deletions stdio-2.js
    Original file line number Diff line number Diff line change
    @@ -15,41 +15,69 @@ The 'stdio' option to child_process.spawn() is an array where each
    index corresponds to a fd in the child. The value is one of the following:
    1. `null` or `undefined` - Use the default value. For 0,1,2,
    this is the same as 'stream'. For any higher value, 'ignore'
    this is the same as 'pipe'. For any higher value, 'ignore'
    2. 'ignore' - Open the fd in the child, and /dev/null it
    3. 'stream' - Create a new dup2 pipe, and expose a stream object to JS.
    The read/write-ability of the stream will be set based on the FD.
    0=read-only, 1,2=write-only, >2=read/write. If a different read/write
    setting is desired, then open a HandleWrap object explicitly.
    4. positive integer - call tty_wrap.guessHandleType. Create the appropriate
    3. 'pipe' - Create a new dup2 pipe, and expose a stream object to JS.
    4. 'ipc' - The same as 'pipe', but set up to send/receive IPC messages and
    pass server/socket/FD handles for use with cluster.
    Note: A ChildProcess may have at most *one* IPC stdio file descriptor.
    Setting this option enables the ChildProcess.send() method. If the
    child writes JSON messages to this file descriptor, then this will trigger
    ChildProcess.on('message'). If the child is a Node.js program, then
    the presents of an IPC channel will enable process.send() and
    process.on('message')
    5. positive integer - call tty_wrap.guessHandleType. Create the appropriate
    pipe_wrap type of thing for this. Note that this will generally not
    be a portable way to share a socket with the child process as a
    stdio fd.
    5. Any HandleWrap object - If the object is a HandleWrap object,
    6. Any HandleWrap object - If the object is a HandleWrap object,
    then the underlying TCP, Pipe, or File handle is shared with the
    child process.
    6. Any {handle:<instanceof HandleWrap>} object - If the object has a
    7. Any {handle:<instanceof HandleWrap>} object - If the object has a
    `handle` member which is a HandleWrap object of some sort, then
    treat this as #5.
    7. Any {fd:<positive integer>} object - If the object has an ingeter `fd` member,
    8. Any {fd:<positive integer>} object - If the object has an ingeter `fd` member,
    then treat this as #4.
    8. Any negative integer: The same as 'stream' (Backwards compatibility with
    9. Any negative integer: The same as 'pipe' (Backwards compatibility with
    customFds.)
    10. Anything else: throw.
    As a shorthand, the `stdio` argument may also be one of the following
    strings, rather than an array:
    * `ignore` --> ['ignore', 'ignore', 'ignore']
    * `stream` --> ['stream', 'stream', 'stream'] == [-1,-1,-1]
    * `pipe` --> ['pipe', 'pipe', 'pipe'] == [-1,-1,-1]
    * `inherit` --> [process.stdin, process.stdout, process.stderr] == [0,1,2]
    Defaults:
    * `spawn`, `exec`, `execFile` --> ['pipe', 'pipe', 'pipe']
    * `fork` --> ['ipc', 'pipe', 'pipe'] or ['ipc', 'ignore', 'ignore'] if
    { silent: true }
    Since cluster just uses child_process.fork, it'll use the same defaults.
    This will require a bit of refactoring in other parts of node besides
    child_process.js:
    * TCP and TTY sockets have a "handle" member added and exposed explicitly.
    * createPipe() should be blessed as a public API so that users can create
    arbitrary duplex streams for stdio.
    function createPipe([options])
    * `options` {Object}
    * `ipc` {Boolean} Default=false
    * `readable` {Boolean} Default=true
    * `writable` {Boolean} Default=true
    By default, all created pipes are duplex. The readable/writable aspect is
    only set at the JavaScript layer to help prevent doing the wrong sort of
    stream.pipe(). At the low-level, there is no special half-duplex behavior.
    IPC is only enabled if explicitly requested, usually by setting 'ipc' as one
    of the stdio FD options.
    */

    // Examples
    @@ -58,15 +86,15 @@ child_process.js:
    spawn(cmd, args, { stdio: 'inherit' })

    // 2. spawn a child, sharing only stderr
    spawn(cmd, args, { stdio: ['stream', 'stream', process.stderr] })
    spawn(cmd, args, { stdio: ['pipe', 'pipe', process.stderr] })

    // 3. since integer FDs will be inspected and set up appropriately,
    // customFds-style will continue to work without modification
    spawn(cmd, args, { stdio: [-1, -1, 2] })

    // 4. Open an extra fd=4, to interact with programs present a
    // startd-style interface.
    spawn(cmd, args, { stdio: ['stream', 'ignore', 'ignore', null, 'stream'] })
    spawn(cmd, args, { stdio: ['pipe', 'ignore', 'ignore', null, 'pipe'] })

    // 5. Provide a socket as the stdin and stdio to a child,
    // to interact with programs that present a cgi-style interface
    @@ -83,20 +111,24 @@ fs.open('error.log', function (er, fd) {
    })

    // 7. like child_process.fork()
    spawn(cmd, args, { stdio: [ 'stream', 1, 2, 'stream' ] })
    spawn(cmd, args, { stdio: [ 'ipc', 1, 2 ] })
    // 7a. like child_process.fork(..., {silent: true})
    spawn(cmd, args, { stdio: [ 'ipc', 'ignore', 'ignore' ] })

    // 8. interact with some silly unix utility that uses a bunch of weird
    // stdio stuff.
    //
    // * stdin is readable and writable
    // * stdin is a pipe
    // * stdout is a JS stream object
    // * stderr is mapped to the parent's stdout
    // * fd=3 is a JS stream object
    // * fd=4 is ignored
    // * fd=5 is the parent's stdin (eg, for the user to type in a pass phrase)
    // * fd=6 is a error log
    // * fd=6 writes to an error log
    // * fd=7 can receive file descriptors and handles.
    var rw = createPipe()
    var ipc = createPipe({ipc: true})
    fs.open("log.txt", function (er, fd) {
    if (er) throw er
    spawn(cmd, args, { stdio: [ rw, 'stream', process.stdout, 'stream', null, 0, fd ] })
    spawn(cmd, args, { stdio: [ rw, 'pipe', process.stdout, 'pipe', null, 0, fd, ipc ] })
    })
  6. isaacs created this gist Apr 3, 2012.
    102 changes: 102 additions & 0 deletions stdio-2.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,102 @@
    /*
    In a child process, each of the stdio streams may be set to
    one of the following:
    1. A new file descriptor in the child, dup2'ed to the parent and
    exposed to JS as a Stream object.
    2. A copy of a file descriptor from the parent, with no other
    added magical stuff.
    3. A black hole - no pipe created.
    The trouble with using raw file descriptor integers for this is
    that they are not portable to Windows.
    The 'stdio' option to child_process.spawn() is an array where each
    index corresponds to a fd in the child. The value is one of the following:
    1. `null` or `undefined` - Use the default value. For 0,1,2,
    this is the same as 'stream'. For any higher value, 'ignore'
    2. 'ignore' - Open the fd in the child, and /dev/null it
    3. 'stream' - Create a new dup2 pipe, and expose a stream object to JS.
    The read/write-ability of the stream will be set based on the FD.
    0=read-only, 1,2=write-only, >2=read/write. If a different read/write
    setting is desired, then open a HandleWrap object explicitly.
    4. positive integer - call tty_wrap.guessHandleType. Create the appropriate
    pipe_wrap type of thing for this. Note that this will generally not
    be a portable way to share a socket with the child process as a
    stdio fd.
    5. Any HandleWrap object - If the object is a HandleWrap object,
    then the underlying TCP, Pipe, or File handle is shared with the
    child process.
    6. Any {handle:<instanceof HandleWrap>} object - If the object has a
    `handle` member which is a HandleWrap object of some sort, then
    treat this as #5.
    7. Any {fd:<positive integer>} object - If the object has an ingeter `fd` member,
    then treat this as #4.
    8. Any negative integer: The same as 'stream' (Backwards compatibility with
    customFds.)
    As a shorthand, the `stdio` argument may also be one of the following
    strings, rather than an array:
    * `ignore` --> ['ignore', 'ignore', 'ignore']
    * `stream` --> ['stream', 'stream', 'stream'] == [-1,-1,-1]
    * `inherit` --> [process.stdin, process.stdout, process.stderr] == [0,1,2]
    This will require a bit of refactoring in other parts of node besides
    child_process.js:
    * TCP and TTY sockets have a "handle" member added and exposed explicitly.
    * createPipe() should be blessed as a public API so that users can create
    arbitrary duplex streams for stdio.
    */

    // Examples

    // 1. spawn a child, taking over the parent's terminal.
    spawn(cmd, args, { stdio: 'inherit' })

    // 2. spawn a child, sharing only stderr
    spawn(cmd, args, { stdio: ['stream', 'stream', process.stderr] })

    // 3. since integer FDs will be inspected and set up appropriately,
    // customFds-style will continue to work without modification
    spawn(cmd, args, { stdio: [-1, -1, 2] })

    // 4. Open an extra fd=4, to interact with programs present a
    // startd-style interface.
    spawn(cmd, args, { stdio: ['stream', 'ignore', 'ignore', null, 'stream'] })

    // 5. Provide a socket as the stdin and stdio to a child,
    // to interact with programs that present a cgi-style interface
    net.createServer(function (conn) {
    spawn(cmd, args, { stdio: [ conn, conn, process.stderr ] })
    }).listen(80)

    // 6. Same as #5, but log stderr to a file:
    fs.open('error.log', function (er, fd) {
    if (er) throw er
    net.createServer(function (conn) {
    spawn(cmd, args, { stdio: [ conn, conn, fd ] })
    }).listen(80)
    })

    // 7. like child_process.fork()
    spawn(cmd, args, { stdio: [ 'stream', 1, 2, 'stream' ] })

    // 8. interact with some silly unix utility that uses a bunch of weird
    // stdio stuff.
    //
    // * stdin is readable and writable
    // * stdout is a JS stream object
    // * stderr is mapped to the parent's stdout
    // * fd=3 is a JS stream object
    // * fd=4 is ignored
    // * fd=5 is the parent's stdin (eg, for the user to type in a pass phrase)
    // * fd=6 is a error log
    var rw = createPipe()
    fs.open("log.txt", function (er, fd) {
    if (er) throw er
    spawn(cmd, args, { stdio: [ rw, 'stream', process.stdout, 'stream', null, 0, fd ] })
    })