writing to child subprocess

124 views
Skip to first unread message

Tapasweni Pathak

unread,
Jun 16, 2018, 1:48:02 PM6/16/18
to nodejs
**Version**: v8.10.0
**Platform**: Darwin Tapaswenis-MBP 16.7.0 Darwin Kernel Version 16.7.0:; root:xnu-3789.73.8~1/RELEASE_X86_64 x86_64


Hi node!

I'm trying to add a string to a child subprocess standard output. I want this code to work without editing the existing test case of main process.

```js
const child = async (command, options, logger) =>
  new Promise((resolve, reject) => {
    const start = Date.now();

    const child = child_process
      .spawn(command, options)
      .on('error', (err) => reject(err))
      .on('exit', (code, signal) => {
        const duration = Date.now() - start;
        resolve({ code, signal, duration });
      });

    child.stdin.write('[some_string]');
    child.stdout.pipe(logger.stream());
    child.stderr.pipe(logger.stream());
  });
```

I started with trying `child.stdout.write`, with `child.stdout.write('some_string')` **I was able to log some_string** in the child subprocess output, **but** my test failed with 

```bash
events.js:183
      throw er; // Unhandled 'error' event
      ^
Error: write EPIPE
    at _errnoException (util.js:992:11)
    at WriteWrap.afterWrite [as oncomplete] (net.js:864:14)
``` 

Now to find out why it is failing I checked if the `EPIPE` is closed for writing with something like, it wasn't. 

```js
process.stdout.on('error', function( err ) {
    if (err.code == "EPIPE") {
        process.exit(0);
    }
});
```

I then logged the properties of `child.stdout` turned out `writable is false`. Same with `stderr.write` for a child subprocess. Another option to write to a child subprocess is using `child.stdin`, I logged its properties but it gave `null`. Fine, but I was able to log the string with it. 


`child.stdout` properties, I can pipe but can't write as `writable` is false? I didn't find any info about this [here](https://nodejs.org/api/child_process.html#child_process_subprocess_stdout).

<details>

```bash
Socket {
      connecting: false,
      _hadError: false,
      _handle: 
       Pipe {
         writeQueueSize: 0,
         owner: [Circular],
         onread: [Function: onread],
         reading: true },
      _parent: null,
      _host: null,
      _readableState: 
       ReadableState {
         objectMode: false,
         highWaterMark: 16384,
         buffer: BufferList { head: null, tail: null, length: 0 },
         length: 0,
         pipes: null,
         pipesCount: 0,
         flowing: null,
         ended: false,
         endEmitted: false,
         reading: true,
         sync: false,
         needReadable: true,
         emittedReadable: false,
         readableListening: false,
         resumeScheduled: false,
         destroyed: false,
         defaultEncoding: 'utf8',
         awaitDrain: 0,
         readingMore: false,
         decoder: null,
         encoding: null },
      readable: true,
      domain: null,
      _events: 
       { end: { [Function: bound onceWrapper] listener: [Function: onend] },
         finish: [Function: onSocketFinish],
         _socketEnd: [Function: onSocketEnd],
         close: [Function] },
      _eventsCount: 4,
      _maxListeners: undefined,
      _writableState: 
       WritableState {
         objectMode: false,
         highWaterMark: 16384,
         finalCalled: false,
         needDrain: false,
         ending: false,
         ended: false,
         finished: false,
         destroyed: false,
         decodeStrings: false,
         defaultEncoding: 'utf8',
         length: 0,
         writing: false,
         corked: 0,
         sync: false,
         bufferProcessing: false,
         onwrite: [Function: bound onwrite],
         writecb: null,
         writelen: 0,
         bufferedRequest: null,
         lastBufferedRequest: null,
         pendingcb: 1,
         prefinished: false,
         errorEmitted: false,
         bufferedRequestCount: 0,
         corkedRequestsFree: 
          { next: null,
            entry: null,
            finish: [Function: bound onCorkedFinish] } },
      writable: false,
      allowHalfOpen: false,
      _bytesDispatched: 8,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: null,
      _server: null,
      [Symbol(asyncId)]: 34495,
      [Symbol(bytesRead)]: 0 }
```
</details>
Now, after adding child.stdin, in my test for the main process I do something like

```js
  const env = process.env;                                                         
  process.env = { fake: 'environment' };                                           
                                                                                   
  sinon.spy(child_process, 'spawn');                                               
  sinon.spy(process.stdout, 'write');                                              
                                                                                   
  try {                                                                            
    await main_process.waitFor();                                                        
  } catch (err) {                                                                  
    assert.ifError(err, 'failed');                                                 
  }                                                                                
                                                                                   
  const process_data = process.stdout.write.args[0][0];                                    
  process.stdout.write.restore();                                               
                                                                                
  assert.equal(                                                                 
    process_data,                                                                       
    '[Fri, 09 Feb 2018 21:57:55 GMT] [another_string] [895ab607-3767-4bbb-bd45-2a3b341cbc46] something\n',
    'abcd'                                                    
  );                                                                            

```

this `process_data` is the main process data that I get(basically before adding `.stdin` was able to get the main process logged data. Now for some reason this has become undefined. Basically when I logged properties of `process.stdout.write`, args is blank, so it is not fetching the args.

Why writing to `.stdin` on a child subprocess is affecting the `process.stdout.write` args of main process? What am I missing here? would appreciate any pointers or even a link if I'm missing something here.

This project I'm working on is open source, can share the whole code if that helps. 
Reply all
Reply to author
Forward
0 new messages