writing to a child subprocess

206 views Asked by At

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

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.

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 below error

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.

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.

 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 }

Now, after adding child.stdin, in my test for the main process I do something like

 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.

0

There are 0 answers